Normal Mapping Problem

Started by
2 comments, last by Striken 16 years, 9 months ago
Hi, im trying to implement normal mapping but i get strange results. As im relatively new with shaders maybe i just misunderstood something. Currently im only experimenting with diffuse lighting and normal mapping. There is one directional light (1.0, 0.0, 0.0) and the non-normal-mapped objects work fine. The Problem is that on the normal mapped objects the diffuse lighting changes when moving the camera and i don't know why. EDIT: It looks right when the camera looks directly onto the normal mapped polygon. Note: type = 2 means normal mapping enabled The Vertex Shader:

#version 110

uniform int type;

varying vec3 sunVec;
varying vec3 halfAngle;
varying vec3 normal;

void main()
{
	vec3 pos = vec3( gl_ModelViewMatrix * gl_Vertex );
	
	if( type == 2 )
	{
		vec3 normal = gl_NormalMatrix * normalize( gl_Normal );
		vec3 tangent = gl_NormalMatrix * normalize( gl_MultiTexCoord1.xyz );
		vec3 binormal = cross( normal, tangent );
		mat3 tSM = mat3( tangent, binormal, normal );
		
		//vec3 sunPos = gl_LightSource[0].position.xyz;
		//vec3 eyeDir = tSM * normalize( -pos );
		sunVec = tSM * ( -normalize( gl_LightSource[0].position.xyz ));
		//sunVec = -normalize(gl_LightSource[0].position.xyz);
		//halfAngle = normalize( eyeDir + sunVec );
	}
	else
	{
		normal = normalize( gl_NormalMatrix * gl_Normal );
		sunVec = -normalize( gl_LightSource[0].position.xyz );
		halfAngle = normalize( normalize( -pos ) + sunVec );
	}
	
	gl_TexCoord[0] = gl_MultiTexCoord0;
	gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}




The Fragment Shader:

#version 110
uniform sampler2D diftex;
uniform sampler2D nortex;
uniform int type;
uniform int lightsnum;

varying vec3 sunVec;
varying vec3 halfAngle;
varying vec3 normal;

void main()
{
	vec4 color;
	vec4 xcol;
	vec3 nnorm;
	color = texture2D( diftex, gl_TexCoord[0].st);
	
	if( color.w == 0.0 )
		discard;
	
	if( type == 2 )
	{
		nnorm = texture2D( nortex, gl_TexCoord[0].st ).xyz;
		nnorm = vec3( 2.0 ) * ( nnorm - vec3( 0.5 ) );
	}
	else
	{
		nnorm = normal;
	}
	
	nnorm = normalize( nnorm );
	float sunDif  = max( dot( normalize(sunVec), nnorm), 0.0 );
	float sunSpec = pow( max( dot( nnorm, normalize(halfAngle) ) , 0.0 ), 32.0 ) * (gl_FrontMaterial.shininess / 64.0 );
	
	color.xyz *= //( gl_LightModel.ambient +
					( gl_LightSource[0].diffuse.xyz * sunDif );
					//( gl_LightSource[0].specular.xyz * sunSpec ) );

	gl_FragColor = color;
}




The Programm Draw Code(pseudocode/shortened):

void Draw()
{
    //lights
    glEnable( GL_LIGHTING );
    float lpos[] = { 1.0, 0.0, 0.0, 0.0 };
    glLightfv( GL_LIGHT0, GL_POSITION, lpos );
    float ldif[] = { 1.0, 1.0, 0.0, 1.0 };
    glLightfv( GL_LIGHT0, GL_DIFFUSE, ldif );
    float lspe[] = { 1.0, 1.0, 1.0, 1.0 };
    glLightfv( GL_LIGHT0, GL_SPECULAR, lspe );
    
    //camera
    glLoadMatrixf( cam.m.m );

    //draw sky

    //check visibility

    //draw meshes
    glEnable( GL_LIGHTING );
    DrawStaticMeshes(); //see below

    //draw even more stuff
}

void DrawStaticMeshes()
{
    for( each object )
    {
            glPushMatrix();

            glTranslatef( object->spaceData->position.x,
                object->spaceData->position.y,
                object->spaceData->position.z );
            glRotatef( object->spaceData->orientation.x, 1.0f, 0.0f, 0.0f );
            glRotatef( object->spaceData->orientation.y, 0.0f, 1.0f, 0.0f );
            glRotatef( object->spaceData->orientation.z, 0.0f, 0.0f, 1.0f );

            DrawStaticMesh( object->staticMeshPtr );

            glPopMatrix();
    }
}



Thanks in advance :)
Advertisement
Hi, I've just finished a normal mapping shader for the first time so I'll try and help you out!

Perhaps the "gl_NormalMatrix" variable is incorrect. I would recommend implementing it just as a basic diffuse lighting shader for the time being, and seeing if that works correctly? Once you get the diffuse lighting sorted, just then use the same matrix that you multiply the incoming normal by, to the tangent and binormal vectors.

"gl_NormalMatrix" should be the object matrix you're applying to the object being rendered. For example:

//frame startglViewport(0, 0, icg::mainWindow->Width(), icg::mainWindow->Height());glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);glClearColor(0.0f, 0.0f, 0.0f, 1.0f);//camera setupglMatrixMode(GL_PROJECTION);						glLoadIdentity();gluPerspective(m_fieldOfView, aspectRatio, m_nearClip, m_farClip);glMatrixMode(GL_MODELVIEW);glLoadIdentity();gluLookAt(cam_pos[X],cam_pos[Y], cam_pos[Z],          cam_pos[X]+cam_fwd[X], cam_pos[Y]+cam_fwd[Y], cam_pos[Z]+cam_fwd[Z],           cam_up[X],  cam_up[Y],  cam_up[Z]);//render objectglPushMatrix();  glMultMatrixf( m_objectMatrix );  model->Render();glPopMatrix();//End frame


For my solution i used the matrix "m_objectMatrix" to make it work correctly.

For your reference, here's my post.
http://www.gamedev.net/community/forums/topic.asp?topic_id=455035

** edit ** NOT the inverse! Just realised I had some redundant, misleading code.

[Edited by - Striken on July 11, 2007 5:54:25 PM]
- Teach a programmer an answer, he can code for a day. Show a programmer the documentation, he can code for a lifetime.
Ok, i tried using my own Matrix.
The result looks somewhat better, but still not all polygons react right.
And there is another thing, gl_LightSource[0].position.xyz changes when moving the camera. As it is a directional light, i need its real values, not the changed ones. Is there a way to get them without using uniforms?

Btw, looking over normal mapping shaders in the web a lot of them look much simpler than mine, maybe i should start from the scratch again...

EDIT: the changed stuff:
In the c++ prog:
void DrawStaticMeshes(){    for( each object )    {            glPushMatrix();            glTranslatef( object->spaceData->position.x,                object->spaceData->position.y,                object->spaceData->position.z );            glRotatef( object->spaceData->orientation.x, 1.0f, 0.0f, 0.0f );            glRotatef( object->spaceData->orientation.y, 0.0f, 1.0f, 0.0f );            glRotatef( object->spaceData->orientation.z, 0.0f, 0.0f, 1.0f );                //NEW NEW NEW NEW NEW:                //compute rotation matrix for normal mapping                if( object->staticMeshPtr->hasNormalMap )                {                    mat4 rotmat, mx;                    mx.Identity();                    mx.Rotatef( object->spaceData->orientation.x, 1.0f, 0.0f, 0.0f );                    rotmat.Rotatef( object->spaceData->orientation.y, 0.0f, 1.0f, 0.0f );                    rotmat = rotmat * mx;                    mx.Identity();                    mx.Rotatef( object->spaceData->orientation.z, 0.0f, 0.0f, 1.0f );                    rotmat = mx * rotmat;                    float mat[9];                    mat[0] = rotmat.m[0]; mat[1] = rotmat.m[1]; mat[2] = rotmat.m[2];                    mat[3] = rotmat.m[4]; mat[4] = rotmat.m[5]; mat[5] = rotmat.m[6];                    mat[6] = rotmat.m[8]; mat[7] = rotmat.m[9]; mat[8] = rotmat.m[10];                    shaderStaticMeshes.SendUniformMat3( "rotMat", mat );                }            DrawStaticMesh( object->staticMeshPtr );            glPopMatrix();    }}


in the vsthe new vs:
// static mesh vertex shader .... per pixel lighting / no normal mapping!!#version 110uniform int type;uniform mat3 rotMat;  //NEW NEW NEWvarying vec3 sunVec;varying vec3 halfAngle;varying vec3 normal;void main(){	vec3 pos = vec3( gl_ModelViewMatrix * gl_Vertex );		normal = gl_NormalMatrix * normalize( gl_Normal );		if( type == 2 )	{		mat3 xmat = rotMat;		normal = rotMat * normalize( gl_Normal ); //tmp		vec3 tangent = xmat * normalize( gl_MultiTexCoord1.xyz );		vec3 binormal = cross( normal, tangent );		mat3 tSM = mat3( tangent, binormal, normal );				//vec3 sunPos = gl_LightSource[0].position.xyz;		//vec3 eyeDir = tSM * normalize( -pos );		sunVec = tSM * ( -normalize( vec3( 1.0, 0.0, 0.0 ) )); //gl_LightSource[0].position.xyz		//sunVec = -normalize(gl_LightSource[0].position.xyz);		//halfAngle = normalize( eyeDir + sunVec );	}	else	{		sunVec = -normalize( gl_LightSource[0].position.xyz );		halfAngle = normalize( normalize( -pos ) + sunVec );	}				gl_TexCoord[0] = gl_MultiTexCoord0;	gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;}
Firstly if "Not All" polygons are reacting correct (but SOME are), then you have to question your calculation of your normal / tangent / binormal for every vertex.

Secondly, I suspect you are applying your light source within the static meshes' own object matrix, therefore it is subject to the rotations and translations of your mesh.

If I were you, pass in either the light source position and from that, calculate the light vector in world space, or just pass in a general light vector (for infinitely far light sources) for simplicity. Which is what I do.

normalize(lightpos - v_in.position);

*Spoiler!* Here's my CG vertx shader code for it, which also includes simple diffuse lighting:

struct vertex{	float4 position		: POSITION;	float4 normal		: NORMAL;	float2 texcoord0	: TEXCOORD0;	float4 tangent   	: TEXCOORD1;	float4 binormal   	: TEXCOORD2;};struct fragment{	float4 position		 : POSITION;	float3 colour		 : COLOR0;	float2 texcoord0	 : TEXCOORD0;	float4 lightVec		 : TEXCOORD2;};uniform float4x4 modelViewProj : state.matrix.mvp;fragment main(vertex IN, uniform float4 lightVector, uniform float4x4 objMatrix){    fragment OUT;    OUT.lightVec        = lightVector;	    IN.normal.w         = 0.0f;    float4 n    	= normalize(mul(objMatrix, IN.normal));    float4 t    	= normalize(mul(objMatrix, IN.tangent));    float4 b    	= normalize(mul(objMatrix, IN.binormal));    float diffuseTerm   = dot( n, OUT.lightVec );    float4x4 TBNMatrix  = float4x4(t, b, n, float4(0.0f, 0.0f, 0.0f, 1.0f));	    ////////////////////////////////////////////////////////////    // Pass the final position on to the fragment shader ///////    ////////////////////////////////////////////////////////////    OUT.lightVec        = mul(TBNMatrix, OUT.lightVec);    OUT.position	= mul(modelViewProj, IN.position);    OUT.texcoord0	= IN.texcoord0;    OUT.colour		= diffuseTerm;    ////////////////////////////////////////////////////////////	    return OUT;}
- Teach a programmer an answer, he can code for a day. Show a programmer the documentation, he can code for a lifetime.

This topic is closed to new replies.

Advertisement