LWJGL Forum

Programming => OpenGL => Topic started by: s_m_w on May 27, 2012, 16:45:01

Title: Bump/Normal Mapping implementation
Post by: s_m_w on May 27, 2012, 16:45:01
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:

(http://i49.tinypic.com/2dkzvd2.png)

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;

}