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;
}