私はチュートリアル用の法線マッピングの実装に取り組んでおり、教育目的で TBN マトリックスを (頂点シェーダーから) フラグメント シェーダーに渡したいので、接線空間の法線ベクトルを照明用のワールド空間に変換できます。計算。法線マッピングは、法線が正の z 方向を指す 2D 平面に適用されます。
ただし、平面の頂点シェーダーで TBN マトリックスを計算すると (したがって、すべての接線/従接線はすべての頂点で同じになります)、表示される法線は完全にオフになります。タンジェント/バイタンジェントと法線ベクトルをフラグメント シェーダーに渡してそこで TBN を構築すると、下の画像に示すように正常に動作します (法線が表示されています)。
ここが変なところです。平面が平坦であるため、T、B、および N ベクトルはすべての頂点で同じであるため、TBN 行列も各フラグメントで同じである必要があります (フラグメント補間は何も変更しないため)。頂点シェーダーの TBN マトリックスは、フラグメント シェーダーの TBN マトリックスとまったく同じである必要がありますが、視覚的な出力はそうではありません。
頂点シェーダーとフラグメント シェーダーのソース コードは次のとおりです。
バーテックス:
#version 330 core
layout (location = 0) in vec3 position;
layout (location = 1) in vec3 normal;
layout (location = 2) in vec2 texCoords;
layout (location = 3) in vec3 tangent;
layout (location = 4) in vec3 bitangent;
out VS_OUT {
vec3 FragPos;
vec3 Normal;
vec2 TexCoords;
vec3 Tangent;
vec3 Bitangent;
mat3 TBN;
} vs_out;
uniform mat4 projection;
uniform mat4 view;
uniform mat4 model;
void main()
{
gl_Position = projection * view * model * vec4(position, 1.0f);
vs_out.FragPos = vec3(model * vec4(position, 1.0));
vs_out.TexCoords = texCoords;
mat3 normalMatrix = transpose(inverse(mat3(model)));
vs_out.Normal = normalize(normalMatrix * normal);
vec3 T = normalize(normalMatrix * tangent);
vec3 B = normalize(normalMatrix * bitangent);
vec3 N = normalize(normalMatrix * normal);
vs_out.TBN = mat3(T, B, N);
vs_out.Tangent = T;
vs_out.Bitangent = B;
}
断片
#version 330 core
out vec4 FragColor;
in VS_OUT {
vec3 FragPos;
vec3 Normal;
vec2 TexCoords;
vec3 Tangent;
vec3 Bitangent;
mat3 TBN;
} fs_in;
uniform sampler2D diffuseMap;
uniform sampler2D normalMap;
uniform vec3 lightPos;
uniform vec3 viewPos;
uniform bool normalMapping;
void main()
{
vec3 normal = fs_in.Normal;
mat3 tbn;
if(normalMapping)
{
// Obtain normal from normal map in range [0,1]
normal = texture(normalMap, fs_in.TexCoords).rgb;
// Transform normal vector to range [-1,1]
normal = normalize(normal * 2.0 - 1.0);
// Then transform normal in tangent space to world-space via TBN matrix
tbn = mat3(fs_in.Tangent, fs_in.Bitangent, fs_in.Normal); // TBN calculated in fragment shader
// normal = normalize(tbn * normal); // This works!
normal = normalize(fs_in.TBN * normal); // This gives incorrect results
}
// Get diffuse color
vec3 color = texture(diffuseMap, fs_in.TexCoords).rgb;
// Ambient
vec3 ambient = 0.1 * color;
// Diffuse
vec3 lightDir = normalize(lightPos - fs_in.FragPos);
float diff = max(dot(lightDir, normal), 0.0);
vec3 diffuse = diff * color;
// Specular
vec3 viewDir = normalize(viewPos - fs_in.FragPos);
vec3 reflectDir = reflect(-lightDir, normal);
vec3 halfwayDir = normalize(lightDir + viewDir);
float spec = pow(max(dot(normal, halfwayDir), 0.0), 32.0);
vec3 specular = vec3(0.2) * spec; // assuming bright white light color
FragColor = vec4(ambient + diffuse + specular, 1.0f);
FragColor = vec4(normal, 1.0); // display normals for debugging
}
両方の TBN 行列は明らかに異なります。以下に、さまざまなフラグメント シェーダー出力の画像をまとめました。
T、B、および N ベクトルが正しく、フラグメント シェーダーのマトリックスも正しいことがわかりますがtbn
、頂点シェーダーの TBN マトリックスfs_in.TBN
は完全に偽の値を示します。
なぜそれが機能しないのか、私にはまったくわかりません。Tangent と Bitangent のベクトルをフラグメント シェーダーに渡すだけで、そこで計算して完了できることはわかっていますが、これが機能しない正確な理由について非常に興味があります。