0
\$\begingroup\$

I'm trying to implement normal mapping on my existing shaders, but I'm definitely doing something wrong, probably related to the TBN matrix. The point is, while drawing two perpendicular walls with normal maps, using a flashlight will work only on the wall facing positive Z, not on the one facing positive X.

Without normal mapping: without normal mapping corner without normal mapping

With normal mapping: with normal mapping corner with normal mapping

I created a "meta" shader, that changes the diffuse, specular and normal source between uniforms, attributes and textures according to defines. Here is the used shader for the walls:

Vertex shader

#version 130

#define DIFFUSE_SOURCE_MATERIAL_MAP 
#define SPECULAR_SOURCE_MATERIAL_COLOR 
#define EMISSIVE_SOURCE_MATERIAL_COLOR 
#define NORMAL_SOURCE_MATERIAL_MAP 
uniform mat4 u_MVPMatrix;

struct PointLight
    {
    vec3 Position;
    vec3 Diffuse, Specular;
    float Intensity;
    float ConstantAttenuation;
    float LinearAttenuation;
    float ExponentialAttenuation;
    };

struct SpotLight
    {
    vec3 Position, Direction;
    vec3 Diffuse, Specular;
    float Intensity;
    float ConstantAttenuation;
    float LinearAttenuation;
    float ExponentialAttenuation;
    float CutOffAngleCosine, CutOffOuterAngleCosine;
    };


in vec3 a_VertexPosition;
in vec2 a_TextureCoords;
in vec4 a_VertexColor;
in vec3 a_VertexNormal;
in vec3 a_Tangent;
in vec3 a_Bitangent;

out vec2 v_TextureCoords;
out vec4 v_VertexColor;
out vec3 v_FragPos;
out vec3 v_Normal;
out vec3 v_TangentPointLightPos[8], v_TangentSpotLightPos[8], v_TangentViewPos, v_TangentFragPos;
out mat3 v_TBN;

uniform mat4 u_ModelMatrix;
uniform mat4 u_ModelTransposeInverse;
uniform mat4 u_ViewMatrix;
uniform vec3 u_ViewPosition;
uniform PointLight u_PointLights[8];
uniform int u_PointLightCount;
uniform SpotLight u_SpotLights[8];
uniform int u_SpotLightCount;

void main()
    {
    // Lighting pre-calculations
    v_FragPos = vec3 ( u_ModelMatrix * vec4(a_VertexPosition, 1.0f));

#if defined ( NORMAL_SOURCE_ATTRIBUTE )        
    v_Normal = mat3 ( u_ModelTransposeInverse ) * a_VertexNormal;
#elif defined ( NORMAL_SOURCE_MATERIAL_MAP )
    // Calculations for the normal map, if needed
    vec3 T = normalize ( mat3 ( u_ModelTransposeInverse ) * a_Tangent );
    vec3 N = normalize ( mat3 ( u_ModelTransposeInverse ) * a_VertexNormal );
    T = normalize(T - dot(T, N) * N);
    vec3 B = cross(N, T);

    v_TBN = transpose ( mat3 ( T, B, N ) );// calculate the inverse TBN. since TBN is an orthogonal matrix, it can be inverted using transpose, which is cheaper on the HW

    for ( int cont = 0; cont < u_PointLightCount; ++cont )
        {
        v_TangentPointLightPos[cont] = v_TBN * u_PointLights[cont].Position;
        }
    for ( int cont = 0; cont < u_SpotLightCount; ++cont )
        {
        v_TangentSpotLightPos[cont] = v_TBN * u_SpotLights[cont].Position;
        }
    v_TangentViewPos = v_TBN * u_ViewPosition;
    v_TangentFragPos = v_TBN * v_FragPos;
#else
    BRONCA NORMAL!!!
#endif

    gl_Position = u_MVPMatrix * vec4(a_VertexPosition,1.0);
    v_TextureCoords = a_TextureCoords;
    v_VertexColor = a_VertexColor;
    }

fragment shader

#version 130
#if __VERSION__ >= 300
out vec4 FragOutput;
#else
vec4 FragOutput;
#endif


#define DIFFUSE_SOURCE_MATERIAL_MAP 
#define SPECULAR_SOURCE_MATERIAL_COLOR 
#define EMISSIVE_SOURCE_MATERIAL_COLOR 
#define NORMAL_SOURCE_MATERIAL_MAP 

struct PointLight
    {
    vec3 Position;
    vec3 Diffuse, Specular;
    float Intensity;
    float ConstantAttenuation;
    float LinearAttenuation;
    float ExponentialAttenuation;
    };

struct SpotLight
    {
    vec3 Position, Direction;
    vec3 Diffuse, Specular;
    float Intensity;
    float ConstantAttenuation;
    float LinearAttenuation;
    float ExponentialAttenuation;
    float CutOffAngleCosine, CutOffOuterAngleCosine;
    };

struct DirectionalLight
    {
    vec3 Direction;
    vec3 Diffuse, Specular;
    float Intensity;
    };

struct AmbientLight
    {
    vec3 Color;
    float Intensity;
    };

in vec4 v_VertexColor;
in vec2 v_TextureCoords;
in vec3 v_FragPos;
in vec3 v_Normal;
in vec3 v_TangentPointLightPos[8], v_TangentSpotLightPos[8], v_TangentViewPos, v_TangentFragPos;
in mat3 v_TBN;

uniform vec3 u_ViewPosition;
uniform PointLight u_PointLights[8];
uniform int u_PointLightCount;
uniform SpotLight u_SpotLights[8];
uniform int u_SpotLightCount;
uniform DirectionalLight u_DirectionalLights[8];
uniform int u_DirectionalLightCount;
uniform AmbientLight u_AmbientLight;

uniform vec4 u_MaterialDiffuseColor;
uniform sampler2D u_MaterialDiffuseMap;
uniform vec3 u_MaterialSpecularColor;
uniform sampler2D u_MaterialSpecularMap;
uniform vec3 u_MaterialEmissiveColor;
uniform sampler2D u_MaterialEmissiveMap;
uniform sampler2D u_MaterialNormalMap;
uniform float u_MaterialShininess;

vec4 GetMaterialDiffuse ( void )
    {
#if defined ( DIFFUSE_SOURCE_MATERIAL_COLOR )
    return u_MaterialDiffuseColor;
#elif defined ( DIFFUSE_SOURCE_ATTRIBUTE )
    return v_VertexColor;
#elif defined ( DIFFUSE_SOURCE_MATERIAL_MAP )
    return texture2D ( u_MaterialDiffuseMap, v_TextureCoords );
#else
    BRONCA DIFFUSE!!!
#endif
    }

vec4 GetMaterialSpecular ( void )
    {
#if defined ( SPECULAR_SOURCE_MATERIAL_COLOR )
    return vec4 ( u_MaterialSpecularColor, 1.0f );
#elif defined ( SPECULAR_SOURCE_MATERIAL_MAP )
    return texture2D ( u_MaterialSpecularMap, v_TextureCoords );
#else
    BRONCA SPECULAR!!!
#endif
    }

vec4 GetMaterialEmissive ( void )
    {
#if defined ( EMISSIVE_SOURCE_MATERIAL_COLOR )
    return vec4 ( u_MaterialEmissiveColor, 1.0f );
#elif defined ( EMISSIVE_SOURCE_MATERIAL_MAP )
    return texture2D ( u_MaterialEmissiveMap, v_TextureCoords );
#else
    BRONCA EMISSIVE!!!
#endif
    }

float GetMaterialShininess ( void )
    {
    return u_MaterialShininess;
    }

vec3 GetFragmentNormal ( void )
    {
#if defined ( NORMAL_SOURCE_ATTRIBUTE )
    return normalize ( v_Normal );
#elif defined ( NORMAL_SOURCE_MATERIAL_MAP )
    vec3 normal = texture2D ( u_MaterialNormalMap, v_TextureCoords ).xyz;
    normal = normalize ( normal * 2.0 - 1.0 );// this is returned in tangent space
    return normal;
#else
    BRONCA NORMAL!!!
#endif
    }

vec3 CalculatePointLightContribution ( int In_Index, vec3 In_LightPosToFrag, vec3 In_ViewPosToFrag, vec3 In_Normal, vec3 In_MaterialDiffuse, vec3 In_MaterialSpecular, float In_MaterialShininess )
    {
    // attenuation
    float distance = length ( In_LightPosToFrag ); // Light to fragment vector
    float attenuation = u_PointLights[In_Index].ConstantAttenuation + u_PointLights[In_Index].LinearAttenuation * distance + u_PointLights[In_Index].ExponentialAttenuation * pow ( distance, 2.0f );

    // diffuse
    vec3 NormalizedLightToFrag = normalize ( In_LightPosToFrag );// normalize, and use just the direction
    float DiffuseComponent = max ( dot ( In_Normal, NormalizedLightToFrag ), 0.0 ); // Calculate dot product on the angle between the two vectors, and make it positive

    // specular
    vec3 NormalizedViewPosToFrag = normalize ( In_ViewPosToFrag );
    vec3 reflectDir = reflect ( -NormalizedLightToFrag, In_Normal );
    vec3 halfwayDir = normalize ( NormalizedLightToFrag + NormalizedViewPosToFrag );
    float SpecularComponent = pow ( max ( dot ( In_Normal, halfwayDir ), 0.0 ), In_MaterialShininess );

    // add it all together
    vec3 DiffuseContribution = DiffuseComponent * u_PointLights[In_Index].Diffuse * In_MaterialDiffuse * u_PointLights[In_Index].Intensity;
    vec3 SpecularContribution = SpecularComponent * u_PointLights[In_Index].Specular * In_MaterialSpecular * u_PointLights[In_Index].Intensity;
    DiffuseContribution /= attenuation;
    SpecularContribution /= attenuation;
    return DiffuseContribution + SpecularContribution;
    }

vec3 CalculateSpotLightContribution ( int In_Index, vec3 In_LightPosToFrag, vec3 In_ViewPosToFrag, vec3 In_Normal, vec3 In_MaterialDiffuse, vec3 In_MaterialSpecular, float In_MaterialShininess )
    {
    // attenuation
    float distance = length ( In_LightPosToFrag ); // Light to fragment vector
    float attenuation = u_SpotLights[In_Index].ConstantAttenuation + u_SpotLights[In_Index].LinearAttenuation * distance + u_SpotLights[In_Index].ExponentialAttenuation * pow ( distance, 2.0f );

    // diffuse
    vec3 NormalizedLightToFrag = normalize ( In_LightPosToFrag );// normalize, and use just the direction
    float DiffuseComponent = max ( dot ( In_Normal, NormalizedLightToFrag ), 0.0 ); // Calculate dot product on the angle between the two vectors, and make it positive

    // specular
    vec3 NormalizedViewPosToFrag = normalize ( In_ViewPosToFrag );
    vec3 reflectDir = reflect ( -NormalizedLightToFrag, In_Normal );
    vec3 halfwayDir = normalize ( NormalizedLightToFrag + NormalizedViewPosToFrag );
    float SpecularComponent = pow ( max ( dot ( In_Normal, halfwayDir ), 0.0 ), In_MaterialShininess );

    // Calculate the intensity derived from the spotlight direction
#if defined ( NORMAL_SOURCE_ATTRIBUTE )
    float theta = dot ( NormalizedLightToFrag, normalize ( -u_SpotLights[In_Index].Direction ) );
#elif defined ( NORMAL_SOURCE_MATERIAL_MAP )
    float theta = dot ( NormalizedLightToFrag, normalize ( -u_SpotLights[In_Index].Direction * v_TBN ) );
#endif
    float epsilon = u_SpotLights[In_Index].CutOffAngleCosine - u_SpotLights[In_Index].CutOffOuterAngleCosine;
    float intensity = clamp ( ( theta - u_SpotLights[In_Index].CutOffOuterAngleCosine ) / epsilon, 0.0, 1.0 );

    // add it all together
    vec3 DiffuseContribution = DiffuseComponent * u_SpotLights[In_Index].Diffuse * In_MaterialDiffuse * u_SpotLights[In_Index].Intensity;
    vec3 SpecularContribution = SpecularComponent * u_SpotLights[In_Index].Specular * In_MaterialSpecular * u_SpotLights[In_Index].Intensity;
    DiffuseContribution /= attenuation;
    SpecularContribution /= attenuation;
    return ( DiffuseContribution + SpecularContribution ) * intensity;
    }

vec3 CalculateDirectionalLightContribution ( int In_Index, vec3 In_Normal, vec3 In_ViewDirection, vec3 In_MaterialDiffuse, vec3 In_MaterialSpecular, float In_MaterialShininess )
    {
    // diffuse
    vec3 NormalizedLightDir = normalize ( -u_DirectionalLights[In_Index].Direction );
    float DiffuseComponent = max ( dot ( In_Normal, NormalizedLightDir ), 0.0 ); // Calculate dot product on the angle between the two vectors, and make it positive

    // specular
    vec3 reflectDir = reflect ( -NormalizedLightDir, In_Normal );
    float SpecularComponent = pow ( max ( dot ( In_ViewDirection, reflectDir ), 0.0 ), In_MaterialShininess );

    // add it all together
    vec3 DiffuseContribution = DiffuseComponent * u_DirectionalLights[In_Index].Diffuse * In_MaterialDiffuse * u_DirectionalLights[In_Index].Intensity;
    vec3 SpecularContribution = SpecularComponent * u_DirectionalLights[In_Index].Specular * In_MaterialSpecular * u_DirectionalLights[In_Index].Intensity;
    return DiffuseContribution + SpecularContribution;
    }

void main ( void )
    {
    vec3 TotalPointLightContribution = vec3 ( 0.0f, 0.0f, 0.0f );
    vec3 TotalDirectionalLightContribution = vec3 ( 0.0f, 0.0f, 0.0f );
    vec3 TotalSpotLightContribution = vec3 ( 0.0f, 0.0f, 0.0f );
    vec3 Diffuse, Specular, Normal;
    float Shininess;

    Diffuse = GetMaterialDiffuse().xyz;
    Specular = GetMaterialSpecular().xyz;
    Shininess = GetMaterialShininess();
    Normal = GetFragmentNormal();

    for ( int cont = 0; cont < u_PointLightCount; ++cont )
        {
#if defined ( NORMAL_SOURCE_ATTRIBUTE )
        vec3 LightPosToFrag = u_PointLights[cont].Position - v_FragPos;
        vec3 ViewPosToFrag = u_ViewPosition - v_FragPos;
#elif defined ( NORMAL_SOURCE_MATERIAL_MAP )
        vec3 LightPosToFrag = v_TangentPointLightPos[cont] - v_TangentFragPos;
        vec3 ViewPosToFrag = v_TangentViewPos - v_TangentFragPos;
#endif
        TotalPointLightContribution += CalculatePointLightContribution ( cont,
                                    LightPosToFrag,
                                    ViewPosToFrag,
                                    Normal,
                                    Diffuse,
                                    Specular,
                                    Shininess );
        }
    for ( int cont = 0; cont < u_SpotLightCount; ++cont )
        {
#if defined ( NORMAL_SOURCE_ATTRIBUTE )
        vec3 LightPosToFrag = u_SpotLights[cont].Position - v_FragPos;
        vec3 ViewPosToFrag = u_ViewPosition - v_FragPos;
#elif defined ( NORMAL_SOURCE_MATERIAL_MAP )
        vec3 LightPosToFrag = v_TangentSpotLightPos[cont] - v_TangentFragPos;
        vec3 ViewPosToFrag = v_TangentViewPos - v_TangentFragPos;
#endif
        TotalSpotLightContribution += CalculateSpotLightContribution ( cont,
                                    LightPosToFrag,
                                    ViewPosToFrag,
                                    Normal,
                                    Diffuse,
                                    Specular,
                                    Shininess );
        }
    for ( int cont = 0; cont < u_DirectionalLightCount; ++cont )
        {
        TotalDirectionalLightContribution += CalculateDirectionalLightContribution ( cont,
                                            Normal,
                                            normalize ( u_ViewPosition - v_FragPos ),
                                            Diffuse,
                                            Specular,
                                            Shininess );

        }

    // Ambient light is calculated here, so it only adds once to the fragment, instead of once every light
    vec3 AmbientLightContribution = u_AmbientLight.Color * u_AmbientLight.Intensity * Diffuse;
    FragOutput = vec4 ( TotalPointLightContribution + TotalSpotLightContribution + TotalDirectionalLightContribution + AmbientLightContribution + GetMaterialEmissive().xyz, GetMaterialDiffuse().a );
#if __VERSION__ < 300
    gl_FragColor = FragOutput;
#endif
    }
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.