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 );ピクセルシェーダーで。

Vertex Shader を介して渡した TBN 値はワールド空間のものです (したがって、gWorldMatrix を乗算した理由)。

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

ワールド=>タンジェント(サーフェス)空間を変換します。

どうしてこれなの?

4

1 に答える 1

5

ラインが必要です

TBN = transpose( TBN ); 

接線空間の法線を右から行列に乗算しているためです。したがって、これは列ベクトルと見なされますが、基底ベクトルは行列の行にあります。したがって、基本変換を適用できるように、行列を転置する必要があります。乗算を次のように切り替える場合は、転置を省略できます。

float3 worldNormal = mul( tangentNormal, TBN );

T、N、および B ベクトルをワールド行列で乗算したため、TBN 行列はタンジェント空間からワールド空間に変換されます (TBN はオブジェクト空間に変換され、そのワールドがワールド空間に変換された後)。他の実装では、TBN にワールド逆転置行列を乗算します。結果の TBN を使用して、ライト ベクトルをワールドから接線空間に変換し、接線法線と比較できます。したがって、TBN が世界を接線空間に変換すると言った人は、このアプローチを使用していると思います (重い行列操作が頂点シェーダーで行われるため、パフォーマンスがいくらか節約されます)。

于 2013-05-15T09:21:26.193 に答える