Search code examples
c++openglglslglm-mathcoordinate-transformation

Normal Mapping Issues


I'm attempting to implement normal mapping into my glsl shaders for the first time. I've written an ObjLoader that calculates the Tangents and Bitangents - I then pass the relevant information to my shaders (I'll show code in a bit). However, when I run the program, my models end up looking like this:

enter image description here

Looks great, I know, but not quite what I am trying to achieve! I understand that I should be simply calculating direction vectors and not moving the vertices - but it seems somewhere down the line I end up making that mistake.

I am unsure if I am making the mistake when reading my .obj file and calculating the tangent/bitangent vectors, or if the mistake is happening within my Vertex/Fragment Shader.

Now for my code:

In my ObjLoader - when I come across a face, I calculate the deltaPositions and deltaUv vectors for all three vertices of the face - and then calculate the tangent and bitangent vectors:

enter image description here

I then organize the vertex data collected to construct my list of indices - and in that process I restructure the tangent and bitangent vectors to respect the newly constructed indice list.

enter image description here

Lastly - I perform Orthogonalization and calcuate the final bitangent vector.

enter image description here

After binding the VAO, VBO, IBO, and passing all the information respectively - my shader calculations are as follows:

Vertex Shader:

void main()
{
    // Output position of the vertex, in clip space
    gl_Position = MVP * vec4(pos, 1.0);

    // Position of the vertex, in world space 
    v_Position = (M * vec4(pos, 0.0)).xyz;

    vec4 bitan = V * M * vec4(bitangent, 0.0);
    vec4 tang = V * M * vec4(tangent, 0.0);
    vec4 norm = vec4(normal, 0.0);

    mat3 TBN = transpose(mat3(tang.xyz, bitan.xyz, norm.xyz));

    // Vector that goes from the vertex to the camera, in camera space
    vec3 vPos_cameraspace = (V * M * vec4(pos, 1.0)).xyz;
    camdir_cameraspace = normalize(-vPos_cameraspace);

    // Vector that goes from the vertex to the light, in camera space
    vec3 lighPos_cameraspace = (V * vec4(lightPos_worldspace, 0.0)).xyz;
    lightdir_cameraspace = normalize((lighPos_cameraspace - vPos_cameraspace));

    v_TexCoord = texcoord;

    lightdir_tangentspace = TBN * lightdir_cameraspace;
    camdir_tangentspace = TBN * camdir_cameraspace;
}

Fragment Shader:

void main()
{
    // Light Emission Properties
    vec3 LightColor = (CalcDirectionalLight()).xyz;
    float LightPower = 20.0;

    // Cutting out texture 'black' areas of texture
    vec4 tempcolor = texture(AlbedoTexture, v_TexCoord);
    if (tempcolor.a < 0.5)
        discard;

    // Material Properties
    vec3 MaterialDiffuseColor = tempcolor.rgb;
    vec3 MaterialAmbientColor = material.ambient * MaterialDiffuseColor;
    vec3 MaterialSpecularColor = vec3(0, 0, 0);

    // Local normal, in tangent space
    vec3 TextureNormal_tangentspace = normalize(texture(NormalTexture, v_TexCoord)).rgb;
    TextureNormal_tangentspace = (TextureNormal_tangentspace * 2.0) - 1.0;

    // Distance to the light
    float distance = length(lightPos_worldspace - v_Position);

    // Normal of computed fragment, in camera space
    vec3 n = TextureNormal_tangentspace;

    // Direction of light (from the fragment)
    vec3 l = normalize(TextureNormal_tangentspace);

    // Find angle between normal and light
    float cosTheta = clamp(dot(n, l), 0, 1);

    // Eye Vector (towards the camera)
    vec3 E = normalize(camdir_tangentspace);

    // Direction in which the triangle reflects the light
    vec3 R = reflect(-l, n);

    // Find angle between eye vector and reflect vector
    float cosAlpha = clamp(dot(E, R), 0, 1);

    color = 
            MaterialAmbientColor + 
            MaterialDiffuseColor * LightColor * LightPower * cosTheta / (distance * distance) +
            MaterialSpecularColor * LightColor * LightPower * pow(cosAlpha, 5) / (distance * distance);
}

Solution

  • I can spot 1 obvious mistake in your code. TBN is generated by the bitangent, tangent and normal. While the bitangent and tangent are transformed from model space to view space, the normal is not transformed. That does not make any sense. All the 3 vetors have to be related to the same coordinate system:

    vec4 bitan = V * M * vec4(bitangent, 0.0);
    vec4 tang  = V * M * vec4(tangent, 0.0);
    vec4 norm  = V * M * vec4(normal, 0.0);
    
    mat3 TBN = transpose(mat3(tang.xyz, bitan.xyz, norm.xyz));