Normal Mapping Gone Horribly Wrong

Open GLES Normal Map Shader Lighting Incorrect

I dont know whats wrong with the above code.
But I wrote a new one, which seems to work.
Maybe this is gonna help someone who wants a simple Open GLES Normal Map Shader:

VP

uniform mat4 worldViewProjMatrix;
uniform vec4 lightDirObjSpace;

attribute vec4 vertex;
attribute vec3 normal;
attribute vec3 tangent;
attribute vec3 binormal;
attribute vec4 uv0;

varying vec2 vTexCoord;
varying vec3 vLightVector;

void main( void )
{
vTexCoord = uv0.st;

vLightVector.x = dot( lightDirObjSpace.xyz, tangent );
vLightVector.y = dot( lightDirObjSpace.xyz, binormal );
vLightVector.z = dot( lightDirObjSpace.xyz, normal );

gl_Position = worldViewProjMatrix * vec4(vertex.xyz, 1.0);
}

FP

uniform vec3 surfaceDiffuseColour;
uniform vec3 surfaceAmbientColour;
uniform sampler2D tex0;

varying vec2 vTexCoord;
varying vec3 vLightVector;

void main(void)
{
vec3 normal = normalize(((texture2D( tex0, vTexCoord ).xyz) * 2.0) - 1.0);

float diffuse = max(0.0, dot(normalize(normal), normalize(vLightVector)));

gl_FragColor = vec4(surfaceDiffuseColour * diffuse + surfaceAmbientColour, 1.0);
}

Normal Mapping Issues

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));

Implementing Normal Mapping using OpenGL/GLSL

That normal map is in tangent-space, but you are treating it as object-space.

You need a bitangent and/or tangent vector per-vertex in addition to your normal in order to form the basis to perform transformation into and out of tangent-space. This matrix is often referred to as simply TBN.

You have two options here:

  1. Transform all of your lighting direction vectors into tangent-space

    • Useful for forward-shading, can be done in a vertex shader
  2. Transform your normal map from tangent-space back to view-space

    • Required by deferred-shading, must be done in fragment shader

Both options require the construction of a TBN matrix, and if your tangent-space basis is orthogonal (modeling software like Assimp can be configured to do this for you) you can transpose the TBN matrix to do either one.

You are implementing forward-shading, so solution #1 is the approach you should take.


Below is a rough overview of the necessary steps for solution #1. Ordinarily you would do the calculation of the lighting direction vector in the vertex shader for better performance.

Vertex Shader to Transform of Lighting Vectors into Tangent-space:

attribute vec3 tangent;
attribute vec3 bitangent;

varying vec3 N;
varying vec3 V;
varying vec3 E;

varying vec3 T;
varying vec3 B;

void main()
{
N = normalize(gl_NormalMatrix*gl_Normal);
V = vec3(gl_ModelViewMatrix*gl_Vertex);
E = normalize(-V);

T = normalize(gl_NormalMatrix*tangent);
B = normalize(gl_NormalMatrix*bitangent);

gl_TexCoord[0] = gl_MultiTexCoord0;
gl_Position = gl_ModelViewProjectionMatrix*gl_Vertex;
}

Fragment Shader to Transform Lighting Vectors into Tangent-space:

varying vec3 N;
varying vec3 V;
varying vec3 E;

varying vec3 B;
varying vec3 T;

uniform sampler2D textureUnit;
uniform sampler2D normalTextureUnit;
uniform vec4 TexColor;

#define MAX_LIGHTS 1

void main()
{
// Construct Tangent Space Basis
mat3 TBN = mat3 (T, B, N);

vec3 normal = normalize (texture2D(normalTextureUnit,gl_TexCoord[0].st).xyz*2.0 - 1.0);

vec4 color = vec4(0,0,0,0);
for(int i = 0; i < MAX_LIGHTS; i++)
{
vec4 lightPos = gl_LightSource[i].position;
vec3 L = lightPos.w > 0 ? lightPos.xyz - V : lightPos;

L *= TBN; // Transform into tangent-space

float dist = length(L);
L = normalize(L);

float NdotL = max(dot(L,N),0.0);
if(NdotL > 0)
{
float att = 1.0;
if(lightPos.w > 0)
{
att = 1.0/ (gl_LightSource[i].constantAttenuation +
gl_LightSource[i].linearAttenuation * dist +
gl_LightSource[i].quadraticAttenuation * dist * dist);
}

vec4 diffuse = clamp(att*NdotL*gl_FrontLightProduct[i].diffuse,0,1);
color += att*gl_FrontLightProduct[i].ambient + diffuse;
}
}

vec4 textureColor = texture2D(textureUnit, vec2(gl_TexCoord[0]));
gl_FragColor = TexColor*textureColor + gl_FrontLightModelProduct.sceneColor + color;
}

There is a tutorial here that should fill in the gaps and explain how to compute tangent and bitangent.

Problem with Parallax Normal Mapping in Opengl , GLSL

Wild guess following

I'd bet either on a tangent basis (vectors) problem, or on view vector being transformed/considered in the wrong coordinate space.

Without more information, it's difficult for me to say more...

Some source code, or animation of the problem would be helpful.



Related Topics



Leave a reply



Submit