Problem with GLSL bump mapping

Hi all, this is my first post here and like almost all first posts the reason for this is a problem I had.
This is my first attempt to use GLSL, I was trying to obtain some bump mapping using shaders. After reading a lot of tutorials I decided that it was time to start writing something.

Unluckily my first attempt wasn’t that good :frowning: the problem as you might see from the image below is that I am able to light only the world ( the textured quad :wink: forget about the box in the middle ) that lies under the light source (the white dot) and only one corner of it (the lower left on the pic). I don’t really know what I’m doing wrong.

 [img]http://img.photobucket.com/albums/v70/_kali_/BumpMapping.jpg[/img] 

here is my vertex program:

varying vec3 LightDir; //The transformed light direction, to pass to the fragment shader
varying vec2 TexCoords;

attribute vec3 Tangent; 

void main() 
{
	gl_Position = ftransform(); 
		
	vec3 n = normalize ( gl_NormalMatrix * gl_Normal);
	vec3 t = normalize ( gl_NormalMatrix * Tangent);
	vec3 b = cross (n, t);
	
	vec3 lPos = gl_LightSource[0].position.xyz - vec3(gl_ModelViewMatrix * gl_Vertex);
	lPos = normalize( lPos );
		
	//Rotate the light into tangent space
	vec3 v;
	v.x = dot ( lPos , t);
	v.y = dot ( lPos , b);
	v.z = dot ( lPos , n);
	LightDir = normalize( v );
		
	TexCoords = gl_MultiTexCoord1.xy;
}

and my fragment program:

uniform sampler2D Texture_Normal; //The bump-map 
uniform sampler2D Texture_Color; //The texture

varying vec3 LightDir; 
varying vec2 TexCoords;
  
void main() 
{
	vec3 lDir = normalize ( LightDir );
	vec3 BumpNorm = vec3(texture2D(Texture_Normal, TexCoords));
	vec3 DecalCol = vec3(texture2D(Texture_Color, TexCoords));
	
	//Expand the bump-map into a normalized unsigned vector float
	BumpNorm = (BumpNorm -0.5) * 2.0;
	
	//Find the dot product between the light direction and the normal
	float NdotL = max(dot(BumpNorm, lDir), 0.0);
	
	vec3 diffuse = NdotL * vec3(1.0) * DecalCol;
	//Set the color of the fragment... 
	gl_FragColor = vec4(diffuse, 1.0);
}

Any hint will be really appreciated.

Edit:
solved… looks like there was a problem with my normal map :frowning: Thank you for your help

If you can upload a .exe and post a link, I will take a look. (I don’t need source if you don’t want to give it)

That is, if someone else does not spot an error first.

Thank you very much sqrt[-1].
you can download the .exe at http://download.yousendit.com/118C9B6A4B91EE33

Extract it in a folder and edit the config.cfg file so that the paths are correct.

You can find the shaders in the data\ folder.

Thank you again, let me know if you have problems running it.

Looking at the results and usage, it seems that you are not setting up the textures correctly.
(This is not to say that the shaders are correct, but the texture binding needs to be fixed before I can look at that)

The draw call looks like this (from GLIntercept):
Tex stage 0 = Diffuse Texture (OK)
Tex stage 1 = White texture?
Tex stage 2 = Heightmap?
Tex stage 3 = White texture?
Tex stage 4 = White texture?

So it seems you are binding a lot of un-necessary textures and not loading/binding the normal map into stage 1.

The problem was exactly that, I was binding the wrong texture.
Thank you sqrt! Its time to learn to use GLIntercept :wink:

Here is the current situation in all its parallax mapped splendour :slight_smile:

Just another question, sorry if I abuse again of your kindness.
While calculating the specular factor is it correct to assume that the eye direction is x=0.0 , y=0.0 , z=-1.0 since I am doing my calculations in eye space?

Also, you should transform the tangent in your vertex program by the modelview matrix, not the normal matrix, which should be used only to transform the normal.
Transforming the tangent by the normal matrix and then normalizing will give you the correct result though, until you start using non uniform scaling.

Originally posted by Kaleidoscope:

Just another question, sorry if I abuse again of your kindness.
While calculating the specular factor is it correct to assume that the eye direction is x=0.0 , y=0.0 , z=-1.0 since I am doing my calculations in eye space?

Yeah, it will be 0,0,-1 ( or 0,0,1 depending on what you are doing).

Also, just looking at your screen shot, it seems there may be other problems as it looks a little funny.

Thank you abrodersen and sqrt for your replies.

@abrodersen:
Thanks for the info, will save me some headaches when (and if) I’ll have to work with non uniform scaling :slight_smile:

@sqrt:
I know there’s something wrong but I cannot tell you what… Maybe is the kind of perspective, maybe that texture fits better with a vertical plane. Or maybe my shaders are wrong ( this theory is far more reliable ).

btw here is my code:
vp:

varying vec3 LightDir; 
varying vec3 EyeDir;
varying vec3 HalfVector;
varying float LightDistance;
varying vec2 TexCoords;

attribute vec3 Tangent; 



//////////////////////////////////////////////////////////////////////////////
// 	Transforms a vector from eye space to tangent space
// 	Parameters:
//		t -> Tangent to the vertex
//		b -> Binormal to the vertex
//		n -> Normal to the vertex
//		vector -> Vector to transform (will be normalized)
//	Output:
//		Vector in tangent space
vec3 TransformToTangentSpace( in vec3 t , in vec3 b , in vec3 n , in vec3 vector )
{
	vec3 v;
	vec3 temp = normalize( vector );
	v.x = dot ( vector , t);
	v.y = dot ( vector , b);
	v.z = dot ( vector , n);
	return normalize(v);
}


void main() 
{
	vec3 lPos;		// Light position ( eye space )
	vec3 halfVec;	// Light half vector
	vec3 vertPos;	// Vertex position ( eye space )
	
	vec3 eyePosition  = vec3( 0.0 , 0.0 , 0.0  );
	vec3 eyeDirection = vec3( 0.0 , 0.0 , -1.0 );
	
	//Put the vertex in the position passed
	gl_Position = ftransform(); 
		
		
	vec3 n = normalize ( gl_NormalMatrix * gl_Normal);
	vec3 t = normalize ( gl_NormalMatrix * Tangent );
	vec3 b = cross (n, t);
	
	 
	vertPos = vec3( gl_ModelViewMatrix * gl_Vertex );
	
	
	lPos 		  = gl_LightSource[0].position.xyz - vertPos;
	LightDistance = length( lPos );
	LightDir 	  = TransformToTangentSpace( t , b, n , lPos );
	
	
	halfVec 	= gl_LightSource[0].halfVector.xyz;
	HalfVector 	= TransformToTangentSpace( t , b, n , halfVec );
	
	
	EyeDir = TransformToTangentSpace( t , b, n , eyeDirection );
	
		
	TexCoords = gl_MultiTexCoord1.xy;
}

fp:

uniform sampler2D Texture_Normal;
uniform sampler2D Texture_Color;
uniform sampler2D Texture_HeightMap;
varying vec3 HalfVector;
varying vec3 EyeDir;
varying vec3 LightDir; 
varying vec2 TexCoords;
varying float LightDistance;
  
const float specularCoeff = 0.6;
  
//////////////////////////////////////////////////////////////////////////////
// 	Computes the diffuse coefficent for the current light
// 	Parameters:
//		lightIdx -> Index of the light to use	
//		d -> Distance form the light from the point ( interpolated from vp )
//	Output:
//		the diffuse coefficent for the current light
vec4 ComputeLightDiffuse( in int lightIdx , in float d)  
{
	float attenuation = 1.0 / ( gl_LightSource[ lightIdx ].constantAttenuation +
								gl_LightSource[ lightIdx ].linearAttenuation * d +
								gl_LightSource[ lightIdx ].quadraticAttenuation * d * d );
	vec4 diffuse = gl_LightSource[ lightIdx ].diffuse * attenuation;
	
	return diffuse;
}
  
//////////////////////////////////////////////////////////////////////////////////////////////
//	Expands a normal read from the bumpmap texture into a normalized vector
//	Parameters:
//		packedNormal -> Bump normal value read from the bumpmap texture
//	Output:
//		Normal unpacked
vec3 UnpackNormal( in vec4 packedNormal )
{
	return ( vec3( packedNormal ) - 0.5) * 2.0;
}
  
  
  
  
void main() 
{
	vec3 	lDir;		// Normalized light direction in tangent space
	vec3 	eDir;		// Normalized eye direction in tangent space
	vec3 	hVec;		// Light half vector
	vec2 	uvCoord;	// Texture coords
	float 	height;		// height offset for parallax mapping
	vec3 	bumpNorm;	// normal to the pixel ( read from the normal map )
	vec4 	decalCol;	// decal color ( read from color map )
	vec4 	diffuse;	// Light diffuse coefficent
	float 	nDotL;		// Dot product between the light direction in tangent space and the pixel normal
	float 	spec;		// Specular factor	
	vec3 	finalCol;	// Final fragment color after the computations	 
		 
		 
	lDir = normalize ( LightDir );		
	eDir = normalize ( EyeDir );		
	hVec = normalize ( HalfVector );
	
	
	uvCoord = TexCoords;
	
	
	// Parallax mapping...
	height = texture2D (Texture_HeightMap, uvCoord ).r;
  	height = height * 0.04 - 0.02;

  	uvCoord = uvCoord + ( eDir.xy * height );
		
		
	bumpNorm = UnpackNormal( texture2D( Texture_Normal, uvCoord ) );
	decalCol = texture2D( Texture_Color, uvCoord );
	
	
	// compute distance between the surface and the light position
	diffuse = ComputeLightDiffuse( 0 , LightDistance ); 
	
	
	//Find the dot product between the light direction and the normal
	nDotL = max( dot( bumpNorm, lDir ), 0.0 );
	
	
	// Specular factor
	spec = max( dot( HalfVector , bumpNorm ), 0.0) ;
	spec = pow( spec, 128.0 ) * specularCoeff;
  		
  		
	finalCol = nDotL * decalCol.rgb;
	finalCol = min( finalCol + spec , 1.0 );


	gl_FragColor = vec4( finalCol , decalCol.a ) * diffuse;
}

if you want to try the .exe you can download the latest version from http://download.yousendit.com/A6D8DCFD26C2FF4F

Extract it in a folder and edit the config.cfg file so that the paths are correct.

You can find the shaders in the data\ folder.

Thank you

The eyeDirection is calculated incorrectly. Use

vec3 eyeDirection = eyePosition - vertPos ;

instead.
The direction can not be constant unless nonlocal viewer is used. This was speedup hack for calculation of specular on older hw and probably not the thing you wish to use with per pixel lighting since it degenerates quality of the specular. For nonlocal viewer your also need to modify the half vector calculation equations.

The paralax mapping is highly dependent on proper direction to work believably so any attempt to use the nonlocal viewer will break that.

The paralax mapping demo in ATI sdk also contains a slightly modified paralax mapping (uses two combined samples from height map, the second sample is taken on offset calculated from first sample) that works much better on sharp edges inside the high map.

When the light is moving above the surface, the lit area is incorrect. This is caused by normalizing the directional vectors within the vertex shader before the interpolation.

Thank you for your help Komat (and sorry for the delay), it looks much better now :slight_smile:
Just another question, the ati sdk you were talking about it the one located at http://www.ati.com/developer/radeonSDK.html (Radeon SDK March 2006)? I want to give that method a try but before downloading 400 m I want to be sure that the download is correct :slight_smile:

Originally posted by Kaleidoscope:
Just another question, the ati sdk you were talking about it the one located at http://www.ati.com/developer/radeonSDK.html (Radeon SDK March 2006)?
Yes, that is the correct sdk. In this latest sdk version the paralax mapping paper and example contains even more variants of the paralax mapping (the modification I was talking about previously is called Paralax mapping with refining in that example).