2013-05-15 59 views
4

我目前在DirectX9的工作,有我的法线贴图如下代码:HLSL法线贴图矩阵乘法

(顶点着色器):

float4x4 gWorldMatrix; 
float4x4 gWorldViewProjectionMatrix; 

float4 gWorldLightPosition; 
float4 gWorldCameraPosition; 

struct VS_INPUT 
{ 
    float4 mPosition : POSITION; 
    float3 mNormal: NORMAL; 
    float3 mTangent: TANGENT; 
    float3 mBinormal: BINORMAL; 
    float2 mUV: TEXCOORD0; 
}; 

struct VS_OUTPUT 
{ 
    float4 mPosition : POSITION; 
    float2 mUV: TEXCOORD0; 
    float3 mLightDir: TEXCOORD1; 
    float3 mViewDir: TEXCOORD2; 
    float3 T: TEXCOORD3; 
    float3 B: TEXCOORD4; 
    float3 N: TEXCOORD5; 
}; 

VS_OUTPUT vs_main(VS_INPUT Input) 
{ 
    VS_OUTPUT Output; 

    Output.mPosition = mul(Input.mPosition, gWorldViewProjectionMatrix); 

    Output.mUV = Input.mUV; 

    float4 worldPosition = mul(Input.mPosition, gWorldMatrix); 

    float3 lightDir = worldPosition.xyz - gWorldLightPosition.xyz; 
    Output.mLightDir = normalize(lightDir); 

    float3 viewDir = normalize(worldPosition.xyz - gWorldCameraPosition.xyz); 
    Output.mViewDir = viewDir; 

    //object space=>world space 
    float3 worldNormal = mul(Input.mNormal, (float3x3)gWorldMatrix); 
    Output.N = normalize(worldNormal); 

    float3 worldTangent = mul(Input.mTangent, (float3x3)gWorldMatrix); 
    Output.T = normalize(worldTangent); 

    float3 worldBinormal = mul(Input.mBinormal, (float3x3)gWorldMatrix); 
    Output.B = normalize(worldBinormal); 

    return Output; 
} 

(像素着色器)

struct PS_INPUT 
{ 
    float2 mUV : TEXCOORD0; 
    float3 mLightDir: TEXCOORD1; 
    float3 mViewDir: TEXCOORD2; 
    float3 T: TEXCOORD3; 
    float3 B: TEXCOORD4; 
    float3 N: TEXCOORD5; 
}; 

sampler2D DiffuseSampler; 
sampler2D SpecularSampler; 
sampler2D NormalSampler; 

float3 gLightColor; 

float4 ps_main(PS_INPUT Input) : COLOR 
{ 
    //read normal from tex 
    float3 tangentNormal = tex2D(NormalSampler, Input.mUV).xyz; 
    tangentNormal = normalize(tangentNormal * 2 - 1); //convert 0~1 to -1~+1. 

    //read from vertex shader 
    float3x3 TBN = float3x3(normalize(Input.T), normalize(Input.B), 
     normalize(Input.N)); //transforms world=>tangent space 

    TBN = transpose(TBN); //transform tangent space=>world 

    float3 worldNormal = mul(TBN, tangentNormal); //note: mat * scalar 
    //(since TBN is row matrix) 

    float3 lightDir = normalize(Input.mLightDir); 
    float3 diffuse = saturate(dot(worldNormal, -lightDir)); 

    float4 albedo = tex2D(DiffuseSampler, Input.mUV); 
    diffuse = gLightColor * albedo.rgb * diffuse; 

    float3 specular = 0; 
    if (diffuse.x > 0) 
    { 
     float3 reflection = reflect(lightDir, worldNormal); 
     float3 viewDir = normalize(Input.mViewDir); 

     specular = saturate(dot(reflection, -viewDir)); 
     specular = pow(specular, 20.0f); 

     //further adjustments to specular (since texture is 2D) 
     float specularIntensity = tex2D(SpecularSampler, Input.mUV); 
     specular *= specularIntensity * gLightColor; 
    } 

    float3 ambient = float3(0.1f, 0.1f, 0.1f) * albedo; 

    return float4(ambient + diffuse + specular, 1); 
} 

该代码有效,但我不太明白为什么我需要做

TBN = transpose(TBN);在像素着色器中。

的TBN值我从顶点着色器传递是那些在世界空间(所以为什么我乘gWorldMatrix),但有人告诉我

float3x3 TBN = float3x3(normalize(Input.T), normalize(Input.B), normalize(Input.N));

改造世界=>切线(面)空间。

这是为什么?

回答

3

您需要的线

TBN = transpose(TBN); 

,因为你从复接到矩阵正确的切空间正常。因此它被认为是列向量,而基向量在矩阵的行中。所以矩阵必须转置,可以应用基本转换。可以省略换位,如果youre切换乘法

float3 worldNormal = mul(tangentNormal, TBN); 

因为你的相乘的T,N和B向量与您的TBN矩阵从切空间到世界空间变换worldmatrix(TBN变换成对象之后,世界转变为世界空间)。其他实现将TBN与世界逆转置矩阵相乘。通过生成的TBN,您可以将来自世界的光矢量转换为切线空间,并将其与正切法线进行比较。所以我认为那个告诉你TBN将世界切换到切线空间的人使用这种方法(它节省了一些性能,因为重型矩阵操作是在顶点着色器中完成的)。

+0

+1所以基本上,如果我没有做转置,并且只是做了'worldNormal = mul(TBN,tangentNormal)',TBN就像一个矩阵,转换world => tangent,因为我将它用作一行主矩阵而不是列主矩阵,对吗? (因此计算不会返回世界空间中的法线,而是一些奇怪的随机值) – dk123

+1

只有反转才会切换转换,转置仅适用于正确的乘法顺序。如果没有转换,你可以将你的向量映射到一个不需要是正交基的基(例如(1,0,0)将被映射到(TX,BX,NX)),这会导致奇怪的伸展或者可能是无稽之谈的结果。但我不是数学家,也许它变成了非常棒的东西;) – Gnietschow

+0

+1谢谢。接下来,我的顶点着色器使用'Output.mPosition = mul(Input.mPosition,gWorldViewProjectionMatrix);'因为ID3DXBaseEffect :: SetMatrix()传入的'gWorldProjectionMatrix'的值是一个行主矩阵,对吗? – dk123