4

私はチュートリアル用の法線マッピングの実装に取り​​組んでおり、教育目的で 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 のベクトルをフラグメント シェーダーに渡すだけで、そこで計算して完了できることはわかっていますが、これが機能しない正確な理由について非常に興味があります。

4

1 に答える 1