我目前正在使用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;
}
(Pixel Shader)
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 =>切线(曲面)空间。
为什么会这样?
答案 0 :(得分:3)
你需要一行
TBN = transpose( TBN );
因为您正在将右边的切线空间法线与矩阵相乘。因此,它被认为是列向量,而基向量在矩阵的行中。因此必须转置矩阵,可以应用基本变换。如果将乘法切换为
,则可以省略转置float3 worldNormal = mul( tangentNormal, TBN );
因为你将T,N和B向量与worldmatrix相乘,你的TBN矩阵从切线空间转换到世界空间(TBN转换为对象空间,之后世界转换为世界空间)。其他实现将TBN与世界逆转置矩阵相乘。使用生成的TBN,您可以将光矢量从世界转换为切线空间,并将其与切线法线进行比较。所以我认为那个告诉你TBN将世界转换为切线空间的人使用这种方法(它节省了一些性能,因为重型矩阵操作是在顶点着色器中完成的。)