I'm trying to implement bump mapping on a cube using OpenGL and GLSL. However, when I rotate my cube around, only the left-facing square and right-facing square appear (that is, in the negative x and positive x direction), the other four faces of the cube (top, bottom, front, back) are black. Here is an example:
My mesh class consists of an indexList and vertexList (the Vertex class contains x, y, z, etc). I'm using this simple cube.ply model, which contains s,t texture coordinates. Borrowing from this example, I calculate the tangent vectors as follows:
void Mesh::computeTangents() {
for (size_t i = 0; i < m_indexList.size(); i += 3) {
Vertex v1 = m_vertexList[m_indexList[i]];
Vertex v2 = m_vertexList[m_indexList[i+1]];
Vertex v3 = m_vertexList[m_indexList[i+2]];
glm::vec3 pos1 = glm::vec3(v1.getX(), v1.getY(), v1.getZ());
glm::vec3 pos2 = glm::vec3(v2.getX(), v2.getY(), v2.getZ());
glm::vec3 pos3 = glm::vec3(v3.getX(), v3.getY(), v3.getZ());
glm::vec2 tex1 = glm::vec2(v1.getS(), v1.getT());
glm::vec2 tex2 = glm::vec2(v2.getS(), v2.getT());
glm::vec2 tex3 = glm::vec2(v3.getS(), v3.getT());
glm::vec3 edge1 = glm::normalize(pos2 - pos1);
glm::vec3 edge2 = glm::normalize(pos3 - pos1);
glm::vec2 texEdge1 = glm::normalize(tex2 - tex1);
glm::vec2 texEdge2 = glm::normalize(tex3 - tex1);
float det = (texEdge1.x * texEdge2.y) - (texEdge1.y * texEdge2.x);
glm::vec3 tangent;
if(fabsf(det) < 1e-6f) {
tangent.x = 1.0;
tangent.y = 0.0;
tangent.z = 0.0;
}
else {
det = 1.0 / det;
tangent.x = (texEdge2.y * edge1.x - texEdge1.y * edge2.x) * det;
tangent.y = (texEdge2.y * edge1.y - texEdge1.y * edge2.y) * det;
tangent.z = (texEdge2.y * edge1.z - texEdge1.y * edge2.z) * det;
glm::normalize(tangent);
}
m_vertexList[m_indexList[i]].setTanX(tangent.x);
m_vertexList[m_indexList[i]].setTanY(tangent.y);
m_vertexList[m_indexList[i]].setTanZ(tangent.z);
m_vertexList[m_indexList[i+1]].setTanX(tangent.x);
m_vertexList[m_indexList[i+1]].setTanY(tangent.y);
m_vertexList[m_indexList[i+1]].setTanZ(tangent.z);
m_vertexList[m_indexList[i+2]].setTanX(tangent.x);
m_vertexList[m_indexList[i+2]].setTanY(tangent.y);
m_vertexList[m_indexList[i+2]].setTanZ(tangent.z);
}
}
If I output the values of the tangent vector for each triangle, I get these values:
1, 0, 0
1, 0, 0
0, 0, -1
0, 0, -1
0, 0, 1
0, 0, 1
-1, 0, 0
-1, 0, 0,
1, 0, 0
1, 0, 0
1, 0, 0
1, 0, 0
If these are correct, then the problem is likely in the shader. My shader is as follows (mostly taken from a book):
vert:
attribute vec4 vertexPosition;
attribute vec3 vertexNormal;
attribute vec2 vertexTexture;
attribute vec3 vertexTangent;
varying vec2 texCoord;
varying vec3 viewDirection;
varying vec3 lightDirection;
uniform vec3 diffuseColor;
uniform float shininess;
uniform vec4 lightPosition;
uniform mat4 modelViewMatrix;
uniform mat4 normalMatrix;
uniform mat4 MVP; // modelview projection
void main() {
vec4 eyePosition = modelViewMatrix * vertexPosition;
vec3 N = normalize(vec3(normalMatrix * vec4(vertexNormal, 1.0)));
vec3 T = normalize(vec3(normalMatrix * vec4(vertexTangent, 1.0)));
vec3 B = normalize(cross(N, T));
vec3 v;
v.x = dot(lightPosition.xyz, T);
v.y = dot(lightPosition.xyz, B);
v.z = dot(lightPosition.xyz, N);
lightDirection = normalize(v);
v.x = dot(eyePosition.xyz, T);
v.y = dot(eyePosition.xyz, B);
v.z = dot(eyePosition.xyz, N);
viewDirection = normalize(v);
texCoord = vertexTexture;
gl_Position = MVP * vertexPosition;
}
Frag:
varying vec2 texCoord;
varying vec3 viewDirection;
varying vec3 lightDirection;
uniform vec3 diffuseColor;
uniform float shininess;
void main() {
float bumpDensity = 16.0;
float bumpSize = 0.15;
vec2 c = bumpDensity * texCoord;
vec2 p = fract(c) - vec2(0.5);
float d, f;
d = dot(p, p);
f = 1.0 / sqrt(d + 1.0);
if (d >= bumpSize) {
p = vec2(0.0);
f = 1.0;
}
vec3 normalDelta = vec3(p.x, p.y, 1.0) * f;
vec3 litColor = diffuseColor * max(dot(normalDelta, lightDirection), 0.0);
vec3 reflectDir = reflect(lightDirection, normalDelta);
float spec = max(dot(viewDirection, reflectDir), 0.0);
spec *= shininess;
litColor = min(litColor + spec, vec3(1.0));
gl_FragColor = vec4(litColor, 1.0);
}
Edit: Changed background to more clearly see the black faces. Also, I incorrectly noted which faces were actually appearing before.
Edit 2: I'm finding that the value of max(dot(normalDelta, lightDirection), 0.0) in the fragment shader returns 0 for those faces. However, I still don't know why.
Edit 3: Well, the problem turned out to be that I was passing the wrong index of the tangent vectors in my Vertex class. That is, I had 10 in this line instead of 9:
glVertexAttribPointer(v3, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)(sizeof( float ) * 9));
Okay, so the problem is that any face with a normal on the xz-plane isn't rendering properly, i.e. the ones that are rendering properly are the ones with normals along the y-axis (top and bottom faces).
And your list of tangents:
Are all either along the x or z axes. So what I'm guessing might be happening is that your normals and tangents are pointing in the same (or opposite) directions, in which case this line in your vertex shader:
is going to result in vec3(0.0, 0.0, 0.0), which can't be normalized.
My recommendation is to try manually giving the x and z faces tangents along the y-axis, see if that makes a difference. If it helps, then you know that the problem is with your computation of the tangents.
If it doesn't help, you can troubleshoot by outputting values from the fragment shader as on-screen colours. Try displaying the viewDirection, lightDirection, normalDelta, reflectDir, and anything else you can think of to see which variable is causing the blackness.
Bonus: Switch the glClear color to something other than black? So that it doesn't look like a face floating in space.