Умножение матриц нормального отображения HLSL

В настоящее время я работаю в 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) );

трансформирует мир => касательное (поверхностное) пространство.

Почему это?

5

Решение

Вам нужна линия

TBN = transpose( TBN );

потому что вы умножаете свою нормаль касательного пространства справа к матрице. Поэтому он считается вектором столбца, а базовые векторы находятся в строках матрицы. Таким образом, матрица должна быть транспонирована, чтобы базовое преобразование могло быть применено. Вы можете опустить транспонирование, если вы переключите умножение на

float3 worldNormal = mul( tangentNormal, TBN );

Поскольку вы умножили вектор T, N и B на мировую матрицу, ваша матрица TBN трансформируется из касательного пространства в мировое пространство (TBN превращается в объектное пространство, после того как этот мир превращается в мировое пространство). Другие реализации умножают TBN на мировую матрицу обратной транспонирования. Получив TBN, вы можете преобразовать вектор света из мира в касательное пространство и сравнить его с касательной нормалью. Поэтому я думаю, что тот, кто сказал вам, что TBN преобразует мир в касательное пространство, использует этот подход (он экономит некоторую производительность, потому что тяжелые операции с матрицами выполняются в вершинном шейдере).

3

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]