Bump/Normal Mapping implementation

Started by s_m_w, May 27, 2012, 16:45:01

Previous topic - Next topic

s_m_w

Hello lwjgl.org community

I've picked up lwjgl a while ago and am currently trying to make my own 3D engine to learn the basics of openGL. After implementing .obj loading, VBO and shader support, I wanted to try bump/normal mapping, but I've come to a point where I have no idea why it's not working. I tried a number of different tutorials and implementations, but all of them ended in the same result:



Here's my current implementation:

Model:
    public void compile() {
        List<Vector3f> redundantVertices = new ArrayList<Vector3f>();
        List<Vector3f> redundantNormals = new ArrayList<Vector3f>();
        List<Vector3f> redundantTextures = new ArrayList<Vector3f>();

        for (Face face : faces) {
            // Each face has the index of vertex position, normal and texture coordinate information in a Vector3f

            int PosIndexVert1 = (int) face.vertexIndices.x - 1;
            int PosIndexVert2 = (int) face.vertexIndices.y - 1;
            int PosIndexVert3 = (int) face.vertexIndices.z - 1;
            
            int NormalIndexVert1 = (int) face.normalIndices.x - 1;
            int NormalIndexVert2 = (int) face.normalIndices.y - 1;
            int NormalIndexVert3 = (int) face.normalIndices.z - 1;
            
            int TexIndexVert1 = (int) face.textureIndices.x - 1;
            int TexIndexVert2 = (int) face.textureIndices.y - 1;
            int TexIndexVert3 = (int) face.textureIndices.z - 1;
            
            // Use the indices to get the actual information and save it

            face.setVertexPosition(0, vertices.get(PosIndexVert1));
            face.setVertexPosition(1, vertices.get(PosIndexVert2));
            face.setVertexPosition(2, vertices.get(PosIndexVert3));
            
            face.setVertexNormal(0, normals.get(NormalIndexVert1));
            face.setVertexNormal(1, normals.get(NormalIndexVert2));
            face.setVertexNormal(2, normals.get(NormalIndexVert3));
            
            face.setVertexTextureCoord(0, texture.get(TexIndexVert1));
            face.setVertexTextureCoord(1, texture.get(TexIndexVert2));
            face.setVertexTextureCoord(2, texture.get(TexIndexVert3));
            
            // Not my own implementation: Tangent calculation

            Vector3f tangent = new Vector3f();

            Vector3f Vert1 = vertices.get(PosIndexVert1);
            Vector3f Vert2 = vertices.get(PosIndexVert2);
            Vector3f Vert3 = vertices.get(PosIndexVert3);

            Vector3f TexCoord1 = texture.get(TexIndexVert1);
            Vector3f TexCoord2 = texture.get(TexIndexVert2);
            Vector3f TexCoord3 = texture.get(TexIndexVert3);

            Vector3f Edge1 = Vector3f.sub(Vert2, Vert1, null);
            Vector3f Edge2 = Vector3f.sub(Vert3, Vert1, null);

            Vector3f Edge1UV = Vector3f.sub(TexCoord2, TexCoord1, null);
            Vector3f Edge2UV = Vector3f.sub(TexCoord3, TexCoord1, null);

            float coef = 1/(Edge1UV.x*Edge2UV.y - Edge2UV.x*Edge2UV.y);

            tangent.x = coef * ((Edge1.x * Edge2UV.y) + (Edge2.x*-Edge2UV.y));
            tangent.y = coef * ((Edge1.y * Edge2UV.y) + (Edge2.y*-Edge2UV.y));
            tangent.z = coef * ((Edge1.z * Edge2UV.y) + (Edge2.z*-Edge2UV.y));
            
            // Is that even correct? Does each vertex of the same face have the same vertex tangent?

            face.setVertexTangent(0, tangent);
            face.setVertexTangent(1, tangent);
            face.setVertexTangent(2, tangent);
        }

        List<Vector3f> Tangents = new ArrayList<Vector3f>();
         
        for (Face face : faces) {
            redundantVertices.add(face.getVertexPositon()[0]);
            redundantVertices.add(face.getVertexPositon()[1]);
            redundantVertices.add(face.getVertexPositon()[2]);
            
            redundantNormals.add(face.getVertexNormal()[0]);
            redundantNormals.add(face.getVertexNormal()[1]);
            redundantNormals.add(face.getVertexNormal()[2]); 

            redundantTextures.add(face.getVertexTextureCoord()[0]);
            redundantTextures.add(face.getVertexTextureCoord()[1]);
            redundantTextures.add(face.getVertexTextureCoord()[2]);
            
            Tangents.add(face.getVertexTangent()[0]);
            Tangents.add(face.getVertexTangent()[1]);
            Tangents.add(face.getVertexTangent()[2]); 
        }

        NumberOfVertices = redundantVertices.size();

        // Turns a list of Vector3fs into a floatbuffer. Works.
        FloatBuffer vertexData = Buffer.getFloatBufferFromArrayList(redundantVertices);
        FloatBuffer normalData = Buffer.getFloatBufferFromArrayList(redundantNormals);
        FloatBuffer textureData = Buffer.getFloatBufferFromArrayList(redundantTextures, true);
        FloatBuffer tangentData = Buffer.getFloatBufferFromArrayList(Tangents);

        vboVertexHandle = glGenBuffers();
        glBindBuffer(GL_ARRAY_BUFFER, vboVertexHandle);
        glBufferData(GL_ARRAY_BUFFER, vertexData, GL_STATIC_DRAW);

        vboNormalHandle = glGenBuffers();
        glBindBuffer(GL_ARRAY_BUFFER, vboNormalHandle);
        glBufferData(GL_ARRAY_BUFFER, normalData, GL_STATIC_DRAW);

        vboTextureHandle = glGenBuffers();
        glBindBuffer(GL_ARRAY_BUFFER, vboTextureHandle);
        glBufferData(GL_ARRAY_BUFFER, textureData, GL_STATIC_DRAW);

        vboTangentHandle = glGenBuffers();
        glBindBuffer(GL_ARRAY_BUFFER, vboTangentHandle);
        glBufferData(GL_ARRAY_BUFFER, tangentData, GL_STATIC_DRAW);

        glBindBuffer(GL_ARRAY_BUFFER, 0);
    }

    public void render() {
        int Shader = ShaderHandler.Shader.BUMP.ProgramHandle;

        glUseProgram(Shader);

        if (TextureHandle != -1) {
            glActiveTexture(GL_TEXTURE0);
            glBindTexture(GL_TEXTURE_2D, TextureHandle);
        } else {
            glActiveTexture(GL_TEXTURE0);
            glBindTexture(GL_TEXTURE_2D, 0);
        }

        if (NormalHandle != -1) {
            glActiveTexture(GL_TEXTURE1);
            glBindTexture(GL_TEXTURE_2D, NormalHandle);
        } else {
            glActiveTexture(GL_TEXTURE1);
            glBindTexture(GL_TEXTURE_2D, 0);
        }

        MaterialHandler.Material.STANDARD.bind();

        glBindBuffer(GL_ARRAY_BUFFER, vboVertexHandle);
        glVertexPointer(3, GL_FLOAT, 0, 0L);

        glBindBuffer(GL_ARRAY_BUFFER, vboNormalHandle);
        glNormalPointer(GL_FLOAT, 0, 0L);

        glBindBuffer(GL_ARRAY_BUFFER, vboTextureHandle);
        glTexCoordPointer(2, GL_FLOAT, 0, 0L);

        // Relay tangent information to shader. I THINK it works.
        int TangentIndex = glGetAttribLocation(Shader, "tangent");

        glBindBuffer(GL_ARRAY_BUFFER, vboTangentHandle);
        glVertexAttribPointer(TangentIndex, 4, GL_FLOAT, false, 0, 0L);
        glEnableVertexAttribArray(TangentIndex);
        // ---

        glEnableClientState(GL_VERTEX_ARRAY);
        glEnableClientState(GL_TEXTURE_COORD_ARRAY);
        glEnableClientState(GL_NORMAL_ARRAY);

        glDrawArrays(GL_TRIANGLES, 0, NumberOfVertices);

        glDisableClientState(GL_COLOR_ARRAY);
        glDisableClientState(GL_TEXTURE_COORD_ARRAY);
        glDisableClientState(GL_NORMAL_ARRAY);

        glUseProgram(0);
    }


Vertex Shader:
attribute vec3 tangent;

	varying vec3 lightVec;
	varying vec3 halfVec;
	varying vec3 eyeVec;
	

void main()
{

	gl_TexCoord[0] =  gl_MultiTexCoord0;
        gl_TexCoord[1] =  gl_MultiTexCoord1;
	
	// Building the matrix Eye Space -> Tangent Space
	vec3 n = normalize (gl_NormalMatrix * gl_Normal);
	vec3 t = normalize (gl_NormalMatrix * tangent);
	vec3 b = cross (n, t);
	
	vec3 vertexPosition = vec3(gl_ModelViewMatrix *  gl_Vertex);
	vec3 lightDir = normalize(gl_LightSource[0].position.xyz - vertexPosition);
		
		
	// transform light and half angle vectors by tangent basis
	vec3 v;
	v.x = dot (lightDir, t);
	v.y = dot (lightDir, b);
	v.z = dot (lightDir, n);
	lightVec = normalize (v);
	
	  
	v.x = dot (vertexPosition, t);
	v.y = dot (vertexPosition, b);
	v.z = dot (vertexPosition, n);
	eyeVec = normalize (v);
	
	
	vertexPosition = normalize(vertexPosition);
	
	/* Normalize the halfVector to pass it to the fragment shader */

	// No need to divide by two, the result is normalized anyway.
	// vec3 halfVector = normalize((vertexPosition + lightDir) / 2.0); 
	vec3 halfVector = normalize(vertexPosition + lightDir);
	v.x = dot (halfVector, t);
	v.y = dot (halfVector, b);
	v.z = dot (halfVector, n);

	// No need to normalize, t,b,n and halfVector are normal vectors.
	//normalize (v);
	halfVec = v ; 
	  
	  
	gl_Position = ftransform();
}

Fragment Shader:
uniform sampler2D diffuseTexture;
	uniform sampler2D normalTexture;
	
	// New bumpmapping
	varying vec3 lightVec;
	varying vec3 halfVec;
	varying vec3 eyeVec;


void main()
{

	// lookup normal from normal map, move from [0,1] to  [-1, 1] range, normalize
	vec3 normal = 2.0 * texture2D (normalTexture, gl_TexCoord[1].st).rgb - 1.0;
	normal = normalize (normal);
	
	// compute diffuse lighting
	float lamberFactor= max (dot (lightVec, normal), 0.0) ;
	vec4 diffuseMaterial = 0.0;
	vec4 diffuseLight  = 0.0;
	
	// compute specular lighting
	vec4 specularMaterial ;
	vec4 specularLight ;
	float shininess ;
  
	// compute ambient
	vec4 ambientLight = gl_LightSource[0].ambient;	
	
	if (lamberFactor > 0.0)
	{
		diffuseMaterial = texture2D (diffuseTexture, gl_TexCoord[0].st);
		diffuseLight  = gl_LightSource[0].diffuse;
		
		//specularMaterial =  vec4(1.0)  ;
		//specularLight = gl_LightSource[0].specular;
		//shininess = pow (max (dot (halfVec, normal), 0.0), 2.0)  ;
		 
		gl_FragColor = diffuseMaterial * diffuseLight * lamberFactor ;
		//gl_FragColor += specularMaterial * specularLight * shininess ;				
	
	}
	
	gl_FragColor +=	ambientLight;
	
}