7

我目前正在使用 directx9 并为我的法线映射提供以下代码:

(顶点着色器):

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) );

变换 world=>tangent(surface) 空间。

为什么是这样?

4

1 回答 1

5

You need the line

TBN = transpose( TBN ); 

because you're multipling your tangent-space normal from the right to the matrix. Therefore it's considered as a column vector, while the base vectors are in the rows of the matrix. So the matrix must be transposed, that the base transformation can be applied. You can omit the transposition, if youre switch the multiplication to

float3 worldNormal = mul( tangentNormal, TBN );

Because your multiplied the T,N and B vector with the worldmatrix your TBN matrix transforms from tangent space to world space (TBN transforms into object-space, after that world transforms into worldspace). Other implementations multiply the TBN with the world inverse transpose matrix. With the resulting TBN you can transform the light vector from world into tangent space and compare it to the tangent normal. So I think the one who told you that TBN transforms world to tangent space uses this approach (It saves some performance, because the heavy matrix-operations are done in the vertexshader).

于 2013-05-15T09:21:26.193 回答