Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Normal mapping and phong shading with incorrect specular component

I'm implementing normal/bump mapping in world space coordinates (I find those easier to work with) and my lighting worked fine without normal mapping, but when introducing normal mapping (and the new vectors calculated with the TBN matrix) the specular component of my lighting is off.

The specular component is not between the camera and the light where it should be so something is wrong. However, looking at my code I could not find any issues. The tangent and bitangent come from the ASSIMP object loader and the eyePos and lightPos are in world coordinates as well.

Since the lighting seems to look correct at the specular section (with the bump mapping shown) I assume it has something to do with the tangent space transformation?

Here is a picture demonstrating the issue:

Bump mapping with wrong specular component

Vertex shader:

#version 330
layout (location = 0) in vec4 vertex;
layout(location = 1) in vec3 normal;
layout(location = 2) in vec3 tangent;
layout(location = 3) in vec3 bitangent;
layout(location = 5) in vec2 texCoord;

uniform mat4 projection;
uniform mat4 view;
uniform mat4 model;
uniform vec3 lightPos; 
uniform vec3 eyePos;

out vec3 Position;
out vec2 TexCoord;

out vec3 tangentLightDir;
out vec3 tangentViewDir;

void main()
{
    gl_Position = projection * view * model * vertex;
    // Position
    Position = vec3(model * vertex);
    // Normal
    mat3 normalMat = transpose(inverse(mat3(model)));
    Normal = normalize(normalMat * normal);
    // Texture
    TexCoord = texCoord;

    // Normal mapping
    mat3 TBN = mat3(tangent, bitangent, Normal);
    TBN = transpose(TBN);
    // Get direction vectors:
    vec3 lightDir = normalize(lightPos - Position);
    vec3 viewDir = normalize(eyePos - Position); 
    // Now transform them to tangent space
    tangentLightDir = TBN * lightDir;
    tangentViewDir = TBN * viewDir;
}

Fragment shader:

#version 330
in vec3 Position;
in vec2 TexCoord;

in vec3 tangentLightDir;
in vec3 tangentViewDir;

uniform sampler2D texture0;
uniform sampler2D texture_normal;

out vec4 outColor;

void main()
{
    // defaults
    vec4 ambient = vec4(0.1);
    vec4 diffuse = vec4(0.4);
    vec4 specular = vec4(0.5);
    vec4 texColor = texture(texture0, TexCoord);    

    // Phong shading
    vec3 LightDir = normalize(tangentLightDir);
    vec3 Norm = normalize(texture(texture_normal, TexCoord).xyz * 2.0 - 1.0);    

    vec3 ViewDir = normalize(tangentViewDir); 
    vec3 ReflectDir = reflect(-LightDir,Norm);
    float specularContribution = pow(max(dot(ViewDir, ReflectDir), 0.0), 32);
    // Calculate diffuse component
    vec4 I = diffuse * max(dot(LightDir, Norm), 0.0);
    diffuse = clamp(I, 0.0, 1.0);
    // Calculate specular component
    specular = specular * specularContribution;

    outColor = texColor * (diffuse + specular + ambient);
}
like image 802
Joey Dewd Avatar asked Feb 24 '14 17:02

Joey Dewd


1 Answers

layout(location = 3) in vec3 bitangent;
layout(location = 5) in vec2 texCoord;

Is 5 that's a correct location?

About TBN matrix. You must (how say jhoffman0x) multiply normalMat matrix to tangent and bitangent, normalize result and after make TBN matrix:

Example from David Wolff - OpenGL 4.0 Shading Language Cookbook. Vertex Shader:

#version 430

layout (location = 0) in vec3 VertexPosition;
layout (location = 1) in vec3 VertexNormal;
layout (location = 2) in vec2 VertexTexCoord;
layout (location = 3) in vec4 VertexTangent;

struct LightInfo {
  vec4 Position;  // Light position in eye coords.
  vec3 Intensity; // A,D,S intensity
};
uniform LightInfo Light;

out vec3 LightDir;
out vec2 TexCoord;
out vec3 ViewDir;

uniform mat4 ModelViewMatrix;
uniform mat3 NormalMatrix;
uniform mat4 ProjectionMatrix;
uniform mat4 MVP;

void main()
{
    // Transform normal and tangent to eye space
    vec3 norm = normalize( NormalMatrix * VertexNormal );
    vec3 tang = normalize( NormalMatrix * vec3(VertexTangent) );
    // Compute the binormal
    vec3 binormal = normalize( cross( norm, tang ) ) * VertexTangent.w;

    // Matrix for transformation to tangent space
    mat3 toObjectLocal = mat3(
        tang.x, binormal.x, norm.x,
        tang.y, binormal.y, norm.y,
        tang.z, binormal.z, norm.z ) ;

    // Transform light direction and view direction to tangent space
    vec3 pos = vec3( ModelViewMatrix * vec4(VertexPosition,1.0) );
    LightDir = normalize( toObjectLocal * (Light.Position.xyz - pos) );

    ViewDir = toObjectLocal * normalize(-pos);

    TexCoord = VertexTexCoord;

    gl_Position = MVP * vec4(VertexPosition,1.0);
}

Fragment Shader:

#version 430

in vec3 LightDir;
in vec2 TexCoord;
in vec3 ViewDir;

layout(binding=0) uniform sampler2D ColorTex;
layout(binding=1) uniform sampler2D NormalMapTex;

struct LightInfo {
  vec4 Position;  // Light position in eye coords.
  vec3 Intensity; // A,D,S intensity
};
uniform LightInfo Light;

struct MaterialInfo {
  vec3 Ka;            // Ambient reflectivity
  vec3 Ks;            // Specular reflectivity
  float Shininess;    // Specular shininess factor
};
uniform MaterialInfo Material;

layout( location = 0 ) out vec4 FragColor;

vec3 phongModel( vec3 norm, vec3 diffR ) {
    vec3 r = reflect( -LightDir, norm );
    vec3 ambient = Light.Intensity * Material.Ka;
    float sDotN = max( dot(LightDir, norm), 0.0 );
    vec3 diffuse = Light.Intensity * diffR * sDotN;

    vec3 spec = vec3(0.0);
    if( sDotN > 0.0 )
        spec = Light.Intensity * Material.Ks *
               pow( max( dot(r,ViewDir), 0.0 ), Material.Shininess );

    return ambient + diffuse + spec;
}

void main() {
    // Lookup the normal from the normal map
    vec4 normal = 2.0 * texture( NormalMapTex, TexCoord ) - 1.0;

    vec4 texColor = texture( ColorTex, TexCoord );
    FragColor = vec4( phongModel(normal.xyz, texColor.rgb), 1.0 );
}
like image 117
Alex Green Avatar answered Oct 10 '22 09:10

Alex Green