Bumpmapping doesn't look right

I am using a directional light, with GLSL and using the following code. The problem is my lighting isn’t looking correct.

//vs

varying vec3 lightDir, halfAngle;
varying vec4 diffuse, ambient;

uniform vec4 lightpos, viewpos;

attribute vec3 vTangent, vBiTangent;

void main()
{   
   vec3 normal = gl_NormalMatrix * normalize(gl_Normal);
   vec3 tangent = gl_NormalMatrix * normalize(vTangent);
   vec3 bitangent = gl_NormalMatrix * normalize(vBiTangent);
        
   vec3 temp = vec3(viewpos - gl_Vertex);
   halfAngle.x = dot(temp, tangent);
   halfAngle.y = dot(temp, bitangent);
   halfAngle.z = dot(temp, normal);

   temp = vec3(lightpos - gl_Vertex);
   lightDir.x = dot(temp, tangent);
   lightDir.y = dot(temp, bitangent);
   lightDir.z = dot(temp, normal);
             
   diffuse = gl_FrontMaterial.diffuse * gl_LightSource[0].diffuse;
   ambient = gl_FrontMaterial.ambient * gl_LightSource[0].ambient;
   ambient += gl_LightModel.ambient * gl_FrontMaterial.ambient;
        
   vec4 realPos = gl_ModelViewMatrix * gl_Vertex;
   projCoord = gl_TextureMatrix[5] * realPos;
    
   gl_TexCoord[0].xy = vec2(gl_MultiTexCoord0);
   
   gl_Position = ftransform();
}

//fs

varying vec3 lightDir, halfAngle;
varying vec4 diffuse, ambient;

uniform sampler2D texture, BumpTex;  

void main()
{
   vec4 lightColor = ambient;
   vec4 modelColor = texture2D(texture, gl_TexCoord[0].xy);
   vec3 lVec = normalize(lightDir);
   vec3 normal = vec3(texture2D(BumpTex, gl_TexCoord[0].xy));

   normal = vec3(2.0) * (normal - vec3(.5));
   normal = normalize(normal);
      
   float NdotL = max(dot(normal, lVec), 0.0);
   lightColor += diffuse * NdotL;
            
   if(NdotL > 0.0)
   {
		vec3 halfVec = normalize(lVec + normalize(halfAngle));
		float NdotHV = max(dot(normal, halfVec), 0.0);
		lightColor += gl_FrontMaterial.specular * gl_LightSource[0].specular * pow(NdotHV, gl_FrontMaterial.shininess);
   }
   
   lightColor = max(lightColor, 0.0);   
   modelColor = modelColor * lightColor;
        
   gl_FragColor = modelColor;
}

any help would be greatly appreciated for I am about to pull all my hair out the last week…

BTW here are the values I get for my TBN

Vertex # 0
Normals 
X 0, Y -1, Z 0
Tangents 
X -1, Y 0, Z 2.34526e-007
Bitangents 
X 2.34526e-007, Y 0, Z 1


Vertex # 1
Normals 
X 0, Y -1, Z 0
Tangents 
X -1, Y 0, Z 2.34526e-007
Bitangents 
X 2.34526e-007, Y 0, Z 1


Vertex # 2
Normals 
X 0, Y -1, Z 0
Tangents 
X -1, Y 0, Z 2.34526e-007
Bitangents 
X 2.34526e-007, Y 0, Z 1


Vertex # 3
Normals 
X 0, Y -1, Z 0
Tangents 
X -1, Y 0, Z 2.34526e-007
Bitangents 
X 2.34526e-007, Y 0, Z 1


Vertex # 4
Normals 
X 0, Y 1, Z 0
Tangents 
X 1, Y 0, Z -2.34526e-007
Bitangents 
X 2.34526e-007, Y 0, Z 1


Vertex # 5
Normals 
X 0, Y 1, Z 0
Tangents 
X 1, Y 0, Z -2.34526e-007
Bitangents 
X 2.34526e-007, Y 0, Z 1


Vertex # 6
Normals 
X 0, Y 1, Z 0
Tangents 
X 1, Y 0, Z -2.34526e-007
Bitangents 
X 2.34526e-007, Y 0, Z 1


Vertex # 7
Normals 
X 0, Y 1, Z 0
Tangents 
X 1, Y 0, Z -2.34526e-007
Bitangents 
X 2.34526e-007, Y 0, Z 1


Vertex # 8
Normals 
X 0, Y 0, Z 1
Tangents 
X 1, Y 0, Z 0
Bitangents 
X 0, Y -1, Z 0


Vertex # 9
Normals 
X 0, Y 0, Z 1
Tangents 
X 1, Y 0, Z 0
Bitangents 
X 0, Y -1, Z 0


Vertex # 10
Normals 
X 0, Y 0, Z 1
Tangents 
X 1, Y 0, Z 0
Bitangents 
X 0, Y -1, Z 0


Vertex # 11
Normals 
X 0, Y 0, Z 1
Tangents 
X 1, Y 0, Z 0
Bitangents 
X 0, Y -1, Z 0


Vertex # 12
Normals 
X 1, Y 0, Z 0
Tangents 
X 0, Y 0, Z -1
Bitangents 
X 0, Y -1, Z 0


Vertex # 13
Normals 
X 1, Y 0, Z 0
Tangents 
X 0, Y 0, Z -1
Bitangents 
X 0, Y -1, Z 0


Vertex # 14
Normals 
X 1, Y 0, Z 0
Tangents 
X 0, Y 0, Z -1
Bitangents 
X 0, Y -1, Z 0


Vertex # 15
Normals 
X 1, Y 0, Z 0
Tangents 
X 0, Y 0, Z -1
Bitangents 
X 0, Y -1, Z 0


Vertex # 16
Normals 
X 0, Y 0, Z -1
Tangents 
X -1, Y 0, Z 0
Bitangents 
X 0, Y -1, Z 0


Vertex # 17
Normals 
X 0, Y 0, Z -1
Tangents 
X -1, Y 0, Z 0
Bitangents 
X 0, Y -1, Z 0


Vertex # 18
Normals 
X 0, Y 0, Z -1
Tangents 
X -1, Y 0, Z 0
Bitangents 
X 0, Y -1, Z 0


Vertex # 19
Normals 
X 0, Y 0, Z -1
Tangents 
X -1, Y 0, Z 0
Bitangents 
X 0, Y -1, Z 0


Vertex # 20
Normals 
X -1, Y 0, Z 0
Tangents 
X 0, Y 0, Z 1
Bitangents 
X 0, Y -1, Z 0


Vertex # 21
Normals 
X -1, Y 0, Z 0
Tangents 
X 0, Y 0, Z 1
Bitangents 
X 0, Y -1, Z 0


Vertex # 22
Normals 
X -1, Y 0, Z 0
Tangents 
X 0, Y 0, Z 1
Bitangents 
X 0, Y -1, Z 0


Vertex # 23
Normals 
X -1, Y 0, Z 0
Tangents 
X 0, Y 0, Z 1
Bitangents 
X 0, Y -1, Z 0

Originally posted by Mars_9999:
I am using a directional light, with GLSL and using the following code. The problem is my lighting isn’t looking correct.

When you ask question about something not looking correctly, you should provide more informations about what the incorrect behaviour is, ideally together with some screenshots.

Your code does not calculate directional light. It calculates a point light without attenuation because direction towards the light depends on position of the vertex.

You are mixing several spaces during your calculation of lightDir and halfAngle vectors. The normal, tangent and bitangent are in eye space. The gl_Vertex is in object space. I do not know in what space the lightpos and viewpos are. You need to ensure that the operations happen in the same space.

THanks for the reply Komat, would you know of any good tutorials on the net that cover Directional bumpmapping? I have looked and all I have found are point.

lightpos is sent into VS as this

lightPosition[0] = -(mapData.map_X * mapData.terrainScaleFactor * .5f + 40.0f);
	lightPosition[1] = ((255 * mapData.terrainScaleFactor) / mapData.heightScaleMeters) + 100.0f;	lightPosition[2] = mapData.map_Z * mapData.terrainScaleFactor * .5f;
	lightPosition[3] = 0.0f;	

and viewpos is

viewpos[0] = camera.pos.x;
	viewpos[1] = camera.pos.y;
	viewpos[2] = camera.pos.z;

the camera.pos variables are the same ones I send to gluLookAt()

this code below I tried before and got nothing to work either. I have been trying for a week to get this working and its driving me nuts.

//fs
varying vec3 lightDir, halfAngle;
varying vec4 diffuse, ambient;

uniform sampler2D texture, BumpTex;  

void main()
{
   vec4 lightColor = ambient;
   vec4 modelColor = texture2D(texture, gl_TexCoord[0].xy);
   vec3 normal = vec3(texture2D(BumpTex, gl_TexCoord[0].xy));
   normal = vec3(2.0) * (normal - vec3(.5));

   float NdotL = max(dot(normal, lightDir), 0.0);
   lightColor += diffuse * NdotL;
            
   if(NdotL > 0.0)
   {
		vec3 halfVec = normalize(normalize(lightDir) + normalize(halfAngle));
		float NdotHV = max(dot(normal, halfVec), 0.0);
		lightColor += gl_FrontMaterial.specular * gl_LightSource[0].specular * pow(NdotHV, gl_FrontMaterial.shininess);
   }
   
   lightColor = max(lightColor, 0.0);   
   modelColor = modelColor * lightColor;
        
   gl_FragColor = modelColor;
}

//vs
varying vec3 lightDir, halfAngle;
varying vec4 diffuse, ambient;

attribute vec3 vTangent, vBiTangent;

void main()
{
   vec3 normal = gl_NormalMatrix * normalize(gl_Normal);
   vec3 tangent = gl_NormalMatrix * normalize(vTangent);
   vec3 bitangent = gl_NormalMatrix * normalize(vBiTangent);
   vec4 position = gl_ModelViewMatrix * gl_Vertex;
      
   mat3 TBN = mat3(tangent, bitangent, normal);
   
   lightDir = TBN * normalize(vec3(gl_LightSource[0].position));
   halfAngle = TBN * vec3(-position);
     
   diffuse = gl_FrontMaterial.diffuse * gl_LightSource[0].diffuse;
   ambient = gl_FrontMaterial.ambient * gl_LightSource[0].ambient;
   ambient += gl_LightModel.ambient * gl_FrontMaterial.ambient;
            
   gl_TexCoord[0].xy = vec2(gl_MultiTexCoord0);
   
   gl_Position = ftransform();
}

Originally posted by Mars_9999:
THanks for the reply Komat, would you know of any good tutorials on the net that cover Directional bumpmapping? I have looked and all I have found are point.

There is no need to special tutorial for directional lights. If you store position/direction in the same way the glLightfv(GL_POSITION) does (w coordinate 1 means point, w coordinate 0 means directional), one code can calculate direction for both cases:

[b]
lightpos is sent into VS as this …

camera.pos variables are the same ones I send to gluLookAt()
[/b]
From your description it looks to me like both those variables are expressed in the world space so the calculation mixes vectors from three different spaces.


this code below I tried before and got nothing to work either. I have been trying for a week to get this working and its driving me nuts.

This older VS code looks more correct to me. It calculates directional light and also it looks like the spaces are the same (if correct modelview matrix was used when the glLightfv(GL_POSITION) was called) however the order of multiplication with the TBN matrix is imho flipped.

Thanks for the reply. I am trying to get everything into eye space… As for the GL_POSITION call I call this right after I call glLoadIdentity() and before I render everything. When I change the modelview matrix I call GL_POSITION to send the values again.

The order of multiplication with the TBN matrix you are referring to

lightDir = TBN * lightDir;
viewDir = TBN * vec3(-eyePosition);

and I need to switch to

lightDir = lightDir * TBN;
viewDir = vec3(-eyePosition) * TBN;

Originally posted by Mars_9999:
As for the GL_POSITION call I call this right after I call glLoadIdentity() and before I render everything. When I change the modelview matrix I call GL_POSITION to send the values again.

When you have the lights in world space you need to do the GL_POSITION call when the modelview matrix contains the world->eye transformation (typically after the gluLookAt call) and not when it contains identity (otherwise the world space coordinates will be interpreted as eye space coordinates) or object->eye transformation for some object (otherwise the world space coordinates will be interpreted as object space coordiantes for that object).

Thanks Komat, I think its working now. So to clarify the order of operations, the last post I have with

lightDir = lightDir * TBN;

is the correct order then…

So why are some tutorials

lightDir = TBN * lightDir;

then? Point lights are the second example?

Thanks

It’s important to understand why this works. The tangent basis vectors form a coordinate system, like any other coordinate system. To describe a vector in that system you need to “dot” it into the coordinate system axes. This can be done by putting the tangent basis axes into the columns of TNB and them multiplying the vector on the left. If the coordinate axes happen to be in the rows, then you can achieve the same result by post-multiplying, or multiplying the vector on the right–same thing. You could also view the change of sides as multiplication with a transposed matrix.

As Minstrel pointed out, the order of multiplications depends on the layout of the matrix.
Both orders might be valid (altrough not at the same time), it only depends on how the matrix is constructed and/or uploaded to the shaders. This might be different in two different tutorials and it does not depend on type of the light.

Originally posted by Komat:
As Minstrel pointed out, the order of multiplications depends on the layout of the matrix.
Both orders might be valid (altrough not at the same time), it only depends on how the matrix is constructed and/or uploaded to the shaders. This might be different in two different tutorials and it does not depend on type of the light.

e.g. DX matrices vs. GL matrices Row column major vs. Column row major?

Originally posted by Mars_9999:
e.g. DX matrices vs. GL matrices Row column major vs. Column row major?
Yes there is difference between DX and GL.
Actually there are two differences. First there is difference in memory layout expected by the function loading matrices into the api. Column-major for OGL, row-major for DX. The second difference is that the DX matrix is transposion of the GL matrix -> the DX has translation part in last row while GL has it in last column.

The side effect is that if you load DX matrix (row major memory layout and translation part in last row) using the glLoadMatrix call, the matrix is transposed into the correct GL format (translation part in last column).

ADDED: In the DX HLSL code the matrix constructors assign values in row major ordering while GLSL assigns the values in column major ordering so even if the code looks the same, it creates matrix that must be multiplied from oposite side.

This topic was automatically closed 183 days after the last reply. New replies are no longer allowed.