Normal mapping: TBN matrix differs by vertex shader result compared to fragment shader

I am working on a regular implementation of mapping for a tutorial and for training purposes. I would like to transfer the TBN matrix to the fragment shader (from the vertex shader) so that I can convert normal vectors into a tangent space to the world - a space for lighting calculation. Normal mapping is applied to a two-dimensional plane with its normal position in the positive z direction.

However, when I calculate the TBN matrix in the vertex shader of the plane plane (so that all tangent / bindent signals are the same for all vertices), the displayed normals are completely disabled. Although, if I transfer the tangent / bindent and normal vectors to the fragment shader and create TBN there, it works very well, as shown in the image below (with normal displayed):

normal mapping wrong

That's where it gets weird. Since the plane is flat, the vectors T, B, and N are the same for all their vertices; therefore, the TBN matrix must also be the same for each fragment (since fragmentary interpolation does not change anything). The TBN matrix in the vertex shader must be exactly the same as the TBN matrix in the fragment shader, but the visual outputs say differently.

The source code for both the vertex and fragment shaders is shown below:

Vertex:

#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 . :

normal mapping different outputs

, T, B N , tbn, TBN fs_in.TBN .

, . , , , , ?

+4
1

:

1) fs_in.Tangent, fs_in.Bitangent fs_in.Normal , , . , .

2) , , , -: bitangent = cross(tangent, normal). () , .

, fs_in.TBN tbn :

, , . , - . , transpose(fs_in.TBN)?

, , [1]! (, , , , , .)

[1] Geeks3D; GLSL 4 × 4; http://www.geeks3d.com/20141114/glsl-4x4-matrix-mat4-fields/

+1

Source: https://habr.com/ru/post/1589491/


All Articles