GLSL lighting. Bad normals? Bad shader? Something else entirely?

This is a discussion on GLSL lighting. Bad normals? Bad shader? Something else entirely? within the Game Programming forums, part of the General Programming Boards category; In my engine, I'm ~trying~ to do per-pixel/normal mapped lighting with GLSL shaders. I'm using a modified shader from RenderMonkey. ...

  1. #1
    The Right Honourable psychopath's Avatar
    Join Date
    Mar 2004
    Location
    Where circles begin.
    Posts
    1,070

    GLSL lighting. Bad normals? Bad shader? Something else entirely?

    In my engine, I'm ~trying~ to do per-pixel/normal mapped lighting with GLSL shaders. I'm using a modified shader from RenderMonkey. Everything compiles properly, but everything is illuminated at full-bright (as if there was no lighting).

    First of all, the shaders:
    ppl2.frag (fragment shader):
    Code:
    uniform float Kd;
    uniform float Ka;
    uniform vec4 diffuse;
    uniform vec4 ambient;
    uniform float Ks;
    uniform vec4 specular;
    uniform float specular_power;
    uniform float bumpiness;
    uniform mat4 view_matrix;
    
    uniform sampler2D base_map;
    
    varying vec2 vTexCoord;
    varying vec3 vLightVector;
    varying vec3 vHalfAngle;
    
    
    
    float saturate( float inValue)
    {
       return clamp(inValue, 0.0, 1.0);
    }
    
    //**---------------------------------------------------------
    //** Function:    main
    //** Description: Declare the main entry point for the shader
    //** Input:       PS_INPUT_STRUCT, derived from the output of
    //**              the associated vertex shader
    //** Returns:     PS_OUTPUT_STRUCT
    //**---------------------------------------------------------
    void main(void)
    {
    
       //**------------------------------------------------------
       //** Retreive the base color and bump components from the
       //** respective textures, based on the passed bump coords.
       //**------------------------------------------------------
       vec3 base = texture2D( base_map, vTexCoord ).xyz;
       vec3 blank = vec3(1.0,1.0,1.0);
    
       //**----------------------------------------------------
       //** Normalize the passed vectors from the vertex shader
       //**----------------------------------------------------
       vec3 normalized_light_vector = normalize( vLightVector );
       vec3 normalized_half_angle   = normalize( vHalfAngle );
      
       //**--------------------------------------------------------
       //** "Smooth out" the bumps based on the bumpiness parameter.
       //** This is simply a linear interpolation between a "flat"
       //** normal and a "bumped" normal.  Note that this "flat"
       //** normal is based on the texture space coordinate basis.
       //**--------------------------------------------------------
       vec3 smooth = vec3(0.5, 0.5, 1.0);
      
       //bump = mix( smooth, bump, bumpiness );
       //bump = normalize( ( bump * 2.0 ) - 1.0 );
    
       //**---------------------------------------------------------
       //** These dot products are used for the lighting model
       //** equations.  The surface normal dotted with the light
       //** vector is denoted by n_dot_l.  The normal vector
       //** dotted with the half angle vector is denoted by n_dot_h.
       //**---------------------------------------------------------
       vec3 n_dot_l = vec3(dot( blank, normalized_light_vector ));
       vec3 n_dot_h = vec3(dot( blank, normalized_half_angle ));
    
       //**--------------------------------------
       //** Calculate the resulting base color,
       //** based on our lighting model.
       //** Ambient + Diffuse + Specular
       //**--------------------------------------
       vec3 color0 = ( base * ambient.xyz * Ka ) +
                     ( base * diffuse.xyz * Kd * max( vec3(0.0), n_dot_l ) ) +
                     ( specular.xyz * Ks * pow( max( vec3(0.0), n_dot_h ), vec3(specular_power) ));
    
       //**------------------------------------
       //** Interpolate the resulting color
       //** based on the reflectance parameter.
       //**------------------------------------
       float color0_a = 1.0; //** Set the alpha component manually
    
    
       gl_FragColor = vec4(color0.xyz, color0_a); //** Return the resulting output struct
    }
    ppl2.vert (vertex shader):
    Code:
    uniform mat4 view_matrix;
    uniform mat4 inv_view_matrix;
    uniform vec4 eyepos;
    uniform vec4 lightpos;
    
    attribute vec3 rm_Tangent; 
    attribute vec3 rm_Binormal;
    
    varying vec2 vTexCoord;
    varying vec3 vLightVector;
    varying vec3 vHalfAngle;
    
    varying vec3 vBasis1;
    varying vec3 vBasis2;
    varying vec3 vBasis3;
    
    varying vec3 vNormal;
    varying vec4 vTangent;
    
    void main( void )
    {
     
       gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
       vec4 eye_position =  gl_ModelViewMatrix * gl_Vertex;
    
       //**----------------------------------------------
       //** Pass the bump and base texture coords through
       //**----------------------------------------------
       vTexCoord = vec2(gl_MultiTexCoord0);
    
       //**----------------------------------------------
       //** Create a local copy of tanspace.
       //**----------------------------------------------
       vec3 tangent  = vec3( rm_Tangent.x,  rm_Tangent.y,  rm_Tangent.z);
       vec3 normal   = vec3(  gl_Normal.x,   gl_Normal.y,   gl_Normal.z);
       vec3 binormal = vec3(rm_Binormal.x, rm_Binormal.y, rm_Binormal.z);
    
       //**--------------------------------------------
       //** Calculate the light vector in object space,
       //** and then transform it into texture space.
       //**--------------------------------------------
       vec3 temp_light_position =  vec3(  vec4(light_position.x, light_position.y, -light_position.z, light_position.w) * inv_view_matrix);
       vec3 temp_light_vector   = temp_light_position.xyz - gl_Vertex.xyz; 
       vLightVector.x = dot( temp_light_vector, tangent );
       vLightVector.y = dot( temp_light_vector, binormal );
       vLightVector.z = dot( temp_light_vector, normal );
    
       //**-------------------------------------------
       //** Calculate the view vector in object space,
       //** and then transform it into texture space.
       //**-------------------------------------------
       vec4 oglEyePos = eye_position;
       oglEyePos.z    = -oglEyePos.z;
       vec3 temp_eye_position = vec3( oglEyePos * inv_view_matrix) ;
       vec3 temp_view_vector  = temp_eye_position - gl_Vertex.xyz;
       vec3 temp_view_vector2;
       temp_view_vector2.x = dot( temp_view_vector, tangent );
       temp_view_vector2.y = dot( temp_view_vector, binormal );
       temp_view_vector2.z = dot( temp_view_vector, normal );
    
       //**-------------------------
       //** Calculate the half angle
       //**-------------------------
       vHalfAngle = vLightVector + temp_view_vector2;
    
    }
    My uniform variables are defined and passed to the shader as:
    Code:
    	float eyepos[4] = {pCam.m_vPosition.x, pCam.m_vPosition.y, pCam.m_vPosition.z, 1}; 									
    	glGetFloatv( GL_PROJECTION_MATRIX, proj );
    	glGetFloatv( GL_MODELVIEW_MATRIX, modl );
    	float kd = 1.0f;
    	float ka = 0.0f;
    	float ks = 0.01f;
    	float specular_power = 6.38780f;
    	float bumpiness = 0.5f;
    	float diffuse[4] = {1.5f, 1.5f, 1.5f, 1.0f};
    	float ambient[4] = {0.0f, 0.0f, 0.0f, 1.0f};
    	float specular[4] = {1.0f, 1.0f, 1.0f, 1.0f};
    	view_matrix == modl;
    	inv_view_matrix[16] == -view_matrix[16];
    Here is where I suspect some of the trouble. I calculate the tangent and bitangent vectors, and store them in an array:
    Code:
    vector3d computeTangentVector(vector3d pVtxA, vector3d pVtxB, vector3d pVtxC, vector3d normal, float tuA, float tvA, float tuB, float tvB, float tuC, float tvC)
    {
    	vector3d vProjAB;
    	vector3d vProjAC;
    	vector3d vTangent;
    	
    	vector3d vAB = pVtxB-pVtxA;
    	vector3d vAC = pVtxC-pVtxA;
    	vector3d nA  = normal;
    
        // Components of vectors to neighboring vertices that are orthogonal to the
        // vertex normal
        vProjAB.x = vAB.x - ( dot(nA, vAB) * nA.x );
    	vProjAB.y = vAB.y - ( dot(nA, vAB) * nA.y );
    	vProjAB.z = vAB.z - ( dot(nA, vAB) * nA.z );
        vProjAC.x = vAC.x - ( dot(nA, vAC) * nA.x );
    	vProjAC.y = vAC.y - ( dot(nA, vAC) * nA.y );
    	vProjAC.z = vAC.z - ( dot(nA, vAC) * nA.z );
    
        // tu texture coordinate differences
        float duAB = tuB - tuA;
        float duAC = tuC - tuA;
    
    	// tv texture coordinate differences
        float dvAB = tvB - tvA;
        float dvAC = tvC - tvA;
    
        if( (duAC * dvAB) > (duAB * dvAC) )
        {
            duAC = -duAC;
            duAB = -duAB;
        }
        
        vTangent.x = (duAC * vProjAB.x) - (duAB * vProjAC.x);
    	vTangent.y = (duAC * vProjAB.y) - (duAB * vProjAC.y);
    	vTangent.z = (duAC * vProjAB.z) - (duAB * vProjAC.z);
        vTangent = normalize(vTangent);
        
    	return vTangent;
    }
    
    extern int index[3];
    void computeTangentsAndBinormals(obj objectP)
    {
    	vector3d v1, v2, v3;
    	vector3d n1, n2, n3;
    
    	vector3d tan1, tan2, tan3;
    	vector3d bitan1, bitan2, bitan3;
    
    	for (int i=0; i<objectP->m_numMeshes; i++)
    	{
    		for ( int j = 0; j < objectP->m_pMeshes[i].m_numTriangles; j++ )
    		{
    			int triangleIndex = objectP->m_pMeshes[i].m_pTriangleIndices[j];
    			const Triangle *pTri = &objectP->m_pTriangles[triangleIndex];
    			
    			index[0] = pTri->m_vertexIndices[0];			
    			index[1] = pTri->m_vertexIndices[1];
    			index[2] = pTri->m_vertexIndices[2];
    
    			v1.x = objectP->m_pVertices[index[0]].m_location[0];
    			v1.y = objectP->m_pVertices[index[0]].m_location[1];
    			v1.z = objectP->m_pVertices[index[0]].m_location[2];
    			v2.x = objectP->m_pVertices[index[1]].m_location[0];
    			v2.y = objectP->m_pVertices[index[1]].m_location[1];
    			v2.z = objectP->m_pVertices[index[1]].m_location[2];
    			v3.x = objectP->m_pVertices[index[2]].m_location[0];
    			v3.y = objectP->m_pVertices[index[2]].m_location[1];
    			v3.z = objectP->m_pVertices[index[2]].m_location[2];
    			n1.x = pTri->m_vertexNormals[0][0];
    			n1.y = pTri->m_vertexNormals[0][1];
    			n1.z = pTri->m_vertexNormals[0][2];
    			n2.x = pTri->m_vertexNormals[1][0];
    			n2.y = pTri->m_vertexNormals[1][1];
    			n2.z = pTri->m_vertexNormals[1][2];
    			n3.x = pTri->m_vertexNormals[2][0];
    			n3.y = pTri->m_vertexNormals[2][1];
    			n3.z = pTri->m_vertexNormals[2][2];
    
    			tan1 = computeTangentVector(v1, v2, v3, n1, 
    				pTri->m_s[0], pTri->m_t[0], pTri->m_s[1], pTri->m_t[1], pTri->m_s[2], pTri->m_t[2]);
    			tan2 = computeTangentVector(v1, v2, v3, n2, 
    				pTri->m_s[0], pTri->m_t[0], pTri->m_s[1], pTri->m_t[1], pTri->m_s[2], pTri->m_t[2]); 
    			tan3 = computeTangentVector(v1, v2, v3, n3, 
    				pTri->m_s[0], pTri->m_t[0], pTri->m_s[1], pTri->m_t[1], pTri->m_s[2], pTri->m_t[2]);
    
    			tan1 = normalize(tan1);
    			tan2 = normalize(tan2);
    			tan3 = normalize(tan3);
    
    			objectP->m_pVertices[index[0]].m_tangent[0] == tan1.x;
    			objectP->m_pVertices[index[0]].m_tangent[1] == tan1.y;
    			objectP->m_pVertices[index[0]].m_tangent[2] == tan1.z;
    			objectP->m_pVertices[index[1]].m_tangent[0] == tan2.x;
    			objectP->m_pVertices[index[1]].m_tangent[1] == tan2.y;
    			objectP->m_pVertices[index[1]].m_tangent[2] == tan2.z;
    			objectP->m_pVertices[index[2]].m_tangent[0] == tan3.x;
    			objectP->m_pVertices[index[2]].m_tangent[1] == tan3.y;
    			objectP->m_pVertices[index[2]].m_tangent[2] == tan3.z;
    
    			bitan1 = cross(n1, tan1);
    			bitan2 = cross(n2, tan2);
    			bitan3 = cross(n3, tan3);
    
    			objectP->m_pVertices[index[0]].m_binormal[0] == bitan1.x;
    			objectP->m_pVertices[index[0]].m_binormal[1] == bitan1.y;
    			objectP->m_pVertices[index[0]].m_binormal[2] == bitan1.z;
    			objectP->m_pVertices[index[1]].m_binormal[0] == bitan2.x;
    			objectP->m_pVertices[index[1]].m_binormal[1] == bitan2.y;
    			objectP->m_pVertices[index[1]].m_binormal[2] == bitan2.z;
    			objectP->m_pVertices[index[2]].m_binormal[0] == bitan3.x;
    			objectP->m_pVertices[index[2]].m_binormal[1] == bitan3.y;
    			objectP->m_pVertices[index[2]].m_binormal[2] == bitan3.z;
    		}
    	}
    }
    And in the draw function, I pass them to the vertex shader as attribute variables:
    Code:
    	for(i=0; i<objectP->m_numVertices; i++)
    	{
    		_ceShader->attribute3fv(program[4], "rm_Tangent", objectP->m_pVertices[i].m_tangent);
    		_ceShader->attribute3fv(program[4], "rm_Binormal", objectP->m_pVertices[i].m_binormal);
    	}

    Alot of code, I know, but I have no idea exactly where the problem is, so posting too much is probably better than posting too little.

    Help will be very much appreciated. This has got me really fusturated!
    Thanx.

    -psychopath
    Memorial University of Newfoundland
    Computer Science

    Mac and OpenGL evangelist.

  2. #2
    Sweet
    Join Date
    Aug 2002
    Location
    Tucson, Arizona
    Posts
    1,803
    void main, are you serious?
    Woop?

  3. #3
    ---
    Join Date
    May 2004
    Posts
    1,379
    It's GLSL not C

  4. #4
    Super Moderator VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,596
    Hmm. Have no idea sand_man. In fact I've been looking for information on tangent vectors and what to use them for and how to compute them. Your code has helped a bit.

    Explain more of what your approach is in general terms and perhaps I can help or at least point you in the right direction. I'm trying to do per-pixel lighting as well, but I'm not doing all the calculations that you are.

    Is this per-pixel, phong, specular lighting or something else?

  5. #5
    ---
    Join Date
    May 2004
    Posts
    1,379
    psychopath is the OP not me
    I wish I knew what he was talking about.

  6. #6
    Super Moderator VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,596
    Oops. Sorry. Need to stop responding to these so late at night.

  7. #7
    The Right Honourable psychopath's Avatar
    Join Date
    Mar 2004
    Location
    Where circles begin.
    Posts
    1,070
    The shader is for normal mapping, however iv'e edited it, so that there is no bump texture, it's just flat per-pixel lighting. Iv'e done this, so I can switch easily between bumped and flat.

    My approach:
    1. I calculate the tangent and bitangent vectors, during the loading of the model.
    2. I pass the vectors to the shader as attribute variables. In GLSL, attribute variables can only be used in the vertex shader, and are used (as the name might suggest) on a per-vertex basis. One of the problems I found though, Is I can't pass the whole array of vectors to the shader, as the shader does things one vertex at a time (one a cycle? i don't really know the terminology). So, I decided to update the vectors in the shader once at the end of the models draw cycle. I'm thinking this may be a bad approach, but I havn't found anything that tells me to do otherwise.

    The math for the tangent calculation, is (loosely) based on this code, from a tutorial I found on emboss bump mapping (I believe it's from codesampler.com):
    Code:
    //-----------------------------------------------------------------------------
    // Name: computeTangentVector()
    // Desc: To find a tangent that heads in the direction of +tv, find
    //       the components of both vectors on the tangent surface, and add a 
    //       linear combination of the two projections that head in the +tv 
    //       direction
    //-----------------------------------------------------------------------------
    vector3f computeTangentVector( Vertex pVtxA, Vertex pVtxB, Vertex pVtxC )
    {
    	vector3f vAB = vector3f(pVtxB.x, pVtxB.y, pVtxB.z) - vector3f(pVtxA.x, pVtxA.y, pVtxA.z);
    	vector3f vAC = vector3f(pVtxC.x, pVtxC.y, pVtxC.z) - vector3f(pVtxA.x, pVtxA.y, pVtxA.z);
    	vector3f nA  = vector3f(pVtxA.nx, pVtxA.ny, pVtxA.nz);
    
        // Components of vectors to neighboring vertices that are orthogonal to the
        // vertex normal
        vector3f vProjAB = vAB - ( dotProduct( nA, vAB ) * nA );
        vector3f vProjAC = vAC - ( dotProduct( nA, vAC ) * nA );
    
        // tu texture coordinate differences
        float duAB = pVtxB.tu - pVtxA.tu;
        float duAC = pVtxC.tu - pVtxA.tu;
    
    	// tv texture coordinate differences
        float dvAB = pVtxB.tv - pVtxA.tv;
        float dvAC = pVtxC.tv - pVtxA.tv;
    
        if( (duAC * dvAB) > (duAB * dvAC) )
        {
            duAC = -duAC;
            duAB = -duAB;
        }
        
        vector3f vTangent = (duAC * vProjAB) - (duAB * vProjAC);
        vTangent.normalize();
        return vTangent;
    }
    
    //-----------------------------------------------------------------------------
    // Name: computeTangentsAndBinormals
    // Desc: For each vertex, create a tangent vector and binormal
    //-----------------------------------------------------------------------------
    void computeTangentsAndBinormals( void )
    {	
        //
        // Even though our simple quad isn't being rendered via an index buffer. 
        // It's useful to think of our geometry as being indexed when it comes time
        // to calculate tangents and binormals. This helps to average tangent 
        // vector across all triangles that make use it, which in turn, produces  
        // much smoother results.
        // 
        // Our quad uses GL_TRIANGLE_STRIP to render, which means that our 
        // quad's four vertices will be indexed like so to create the two 
        // triangles required to create the quad:
        //
        // Tri #1 = 0, 1, 2
        // Tri #2 = 2, 3, 1
        //
    
        const int nNumIndices = 6;
    	int indices[nNumIndices] = { 0,1,2,  2,3,1 };
    
        //
    	// For every triangle or face, use the indices to find the vertices 
    	// that make it up. Then, compute the tangent vector for each one,
    	// averaging whenever a vertex happens to be shared amongst triangles.
        //
    
        for( int i = 0; i < nNumIndices; i += 3 )
        {
    		int a = indices[i+0];
            int b = indices[i+1];
            int c = indices[i+2];
    
            // We use += because we want to average the tangent vectors with 
            // neighboring triangles that share vertices.
    		g_vTangents[a] += computeTangentVector( g_quadVertices[a], g_quadVertices[b], g_quadVertices[c] );
    	    g_vTangents[b] += computeTangentVector( g_quadVertices[b], g_quadVertices[a], g_quadVertices[c] );
    	    g_vTangents[c] += computeTangentVector( g_quadVertices[c], g_quadVertices[a], g_quadVertices[b] );
    	}
    
        //
        // Normalize each tangent vector and create a binormal to pair with it...
        //
    
        for( i = 0; i < NUM_VERTICES; ++i )
        {
    		g_vTangents[i].normalize();
    
    		g_vBiNormals[i] = crossProduct( vector3f( g_quadVertices[i].nx, g_quadVertices[i].ny, g_quadVertices[i].nz ), g_vTangents[i] );
        }
    }
    Memorial University of Newfoundland
    Computer Science

    Mac and OpenGL evangelist.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. GLSL lighting (again)
    By psychopath in forum Game Programming
    Replies: 6
    Last Post: 12-19-2005, 12:34 PM
  2. Light woes or bad normals?
    By VirtualAce in forum Game Programming
    Replies: 11
    Last Post: 10-10-2005, 01:21 PM

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21