I am implimenting normal mapping in my program and must calculate the tangent for use in the shader. So i used terathon.com's method of calculating the tangents, along with code to average the tangents of vertices that are shared among mayn faces (I'm using VA at the moment). And here is the code:
void nrgObject::makeTanArray(){
float x1, x2, y1, y2, z1, z2, s1, s2, t1, t2, r, NdotT, BdotT;
NORMAL sdir, tdir, tTan, NcrossT;
TANGENT tempTangent;
std::vector<TANGENT> allTangents;
// for each triangle
std::vector<GEOM_INDEX>::size_type triIndex = std::vector<GEOM_INDEX>::size_type(0);
while(triIndex < index.size()){
GEOM_INDEX tV2 = index[triIndex];
x1 = rVerts[tV2.v2].x - rVerts[tV2.v1].x;
x2 = rVerts[tV2.v3].x - rVerts[tV2.v1].x;
y1 = rVerts[tV2.v2].y - rVerts[tV2.v1].y;
y2 = rVerts[tV2.v3].y - rVerts[tV2.v1].y;
z1 = rVerts[tV2.v2].z - rVerts[tV2.v1].z;
z2 = rVerts[tV2.v3].z - rVerts[tV2.v1].z;
s1 = rVerts[tV2.v2].u - rVerts[tV2.v1].u;
s2 = rVerts[tV2.v3].u - rVerts[tV2.v1].u;
t1 = rVerts[tV2.v2].v - rVerts[tV2.v1].v;
t2 = rVerts[tV2.v3].v - rVerts[tV2.v1].v;
r = 1.0f / (s1*t2 - s2*t1);
sdir.i = (t2*x1 - t1*x2)*r;
sdir.j = (t2*y1 - t1*y2)*r;
sdir.k = (t2*z1 - t1*z2)*r;
tdir.i = (s1*x2 - s2*x1)*r;
tdir.j = (s1*y2 - s2*y1)*r;
tdir.k = (s1*z2 - s2*z1)*r;
// perform Gram-Schmidt orthogonalize for each vertex of face
// sdir - normal
tTan.i = sdir.i - rVerts[tV2.v1].i;
tTan.j = sdir.j - rVerts[tV2.v1].j;
tTan.k = sdir.k - rVerts[tV2.v1].k;
// dot(normal, sdir)
NdotT = (rVerts[tV2.v1].i * sdir.i) + (rVerts[tV2.v1].j * sdir.j)
+ (rVerts[tV2.v1].k * sdir.k);
// (sdir - n)*NdotT
tTan.i *= NdotT;
tTan.j *= NdotT;
tTan.k *= NdotT;
// normalize
tTan.i /= sqrtf((tTan.i * tTan.i) + (tTan.j * tTan.j) + (tTan.k * tTan.k));
tTan.j /= sqrtf((tTan.i * tTan.i) + (tTan.j * tTan.j) + (tTan.k * tTan.k));
tTan.k /= sqrtf((tTan.i * tTan.i) + (tTan.j * tTan.j) + (tTan.k * tTan.k));
// normal X sdir = Bitangent
NcrossT.i = rVerts[tV2.v1].j*sdir.k - rVerts[tV2.v1].k*sdir.j;
NcrossT.j = rVerts[tV2.v1].k*sdir.i - rVerts[tV2.v1].i*sdir.k;
NcrossT.k = rVerts[tV2.v1].i*sdir.j - rVerts[tV2.v1].j*sdir.i;
// dot(bitangent, tdir)
BdotT = (NcrossT.i * tdir.i) + (NcrossT.j * tdir.j)
+ (NcrossT.k * tdir.k);
tempTangent.s = tTan.i;
tempTangent.t = tTan.j;
tempTangent.r = tTan.k;
tempTangent.h = (BdotT < 0.0f) ? -1.0f : 1.0f; // handedness
tempTangent.vert = tV2.v1; // vertex tangetn belongs to
allTangents.push_back(tempTangent); // add it to vector of tangents
// which come out to # of faces
// times 3 (trianlge)
// repeat the code above for the other two vertices
triIndex++;
}
// for each face (each index points to 3 vertices to make on face)
triIndex = std::vector<GEOM_INDEX>::size_type(0);
while(triIndex < index.size()){
GEOM_INDEX tV2 = index[triIndex];
int tanCount;
std::vector<TANGENT> tempTan;
std::vector<TANGENT>::size_type aTans;
// rVerts[iterator].h was set to -2.0 since handedness can't equal anything
// other then 1.0 or 1.0, so if the vertex's tangent wasn't computed,
// compute it
if(rVerts[tV2.v1].h == -2.0f){
// reset variavles
tempTangent.s = 0.0f;
tempTangent.t = 0.0f;
tempTangent.r = 0.0f;
tempTangent.h = -1.0f;
//tempTangent.vert = -1.0f;
tempTan.clear();
aTans = std::vector<TANGENT>::size_type(0);
tanCount = 0;
// search through vector for all tangents that belong to the current vertex
while(aTans < allTangents.size()){
TANGENT tT = allTangents[aTans];
// if the index numbers match, add it to the vector used to average
// the tangents
if(tT.vert == tV2.v1)
tempTan.push_back(tT);
aTans++;
}
// for all of the tangents of the vertex...
aTans = std::vector<TANGENT>::size_type(0);
for(aTans = 0; aTans < tempTan.size(); aTans++){
// add them up
tempTangent.s += tempTan[aTans].s;
tempTangent.t += tempTan[aTans].t;
tempTangent.r += tempTan[aTans].r;
// um, not sure if this is right :S
tempTangent.h *= tempTan[aTans].h;
tanCount++; // keep count of the number of tangents
}
// average the components
tempTangent.s /= float(tanCount);
tempTangent.t /= float(tanCount);
tempTangent.r /= float(tanCount);
// set the current vertex's tangent info
rVerts[tV2.v1].s = tempTangent.s;
rVerts[tV2.v1].t = tempTangent.t;
rVerts[tV2.v1].r = tempTangent.r;
rVerts[tV2.v1].h = tempTangent.h;
}
// same as above for the other 2 vertices would go here
triIndex++;
}
}
Sorry for the lack of vector classes. I then pass those tangents in the vertex array via GL_TEXTURE_COORD_ARRAY. Then, in the vertex shader, I calculate the bitangent, multipling it by the handedness. In the fragment shader, I use the normal map as the normal of the fragment. My shader code (note: code taken from an online tutorial and modified):
Vert shader:
varying vec4 lightDir;
varying vec3 normal, halfVector, spotDir;
void main()
{
vec3 tempLight;
// computing TBN matrix for tangent space ****************************************************
vec3 v_Normal = normalize(gl_NormalMatrix*gl_Normal); // normal to eye space
vec3 v_Tangent = normalize(gl_NormalMatrix*gl_MultiTexCoord7.xyz); // tangent to eye space
vec3 v_Binormal = cross(v_Normal, v_Tangent) * gl_MultiTexCoord7.w; // binormal to eye space
mat3 tangentBasis = mat3( // in column major order
v_Tangent.x, v_Binormal.x, v_Normal.x,
v_Tangent.y, v_Binormal.y, v_Normal.y,
v_Tangent.z, v_Binormal.z, v_Normal.z);
// compute light vector ***********************************************************************
vec4 ecPos, bb;
vec3 aux;
ecPos = gl_ModelViewMatrix * gl_Vertex;
aux = vec3(gl_LightSource[1].position-ecPos);
tempLight = aux;
// compute normal and half vector **************************************************************
normal = normalize(gl_NormalMatrix * gl_Normal);// vertex to eye coordinates
halfVector = normalize(gl_LightSource[1].halfVector.xyz);
// convert coordinates to tangent space ********************************************************
tempLight = tangentBasis * tempLight;
halfVector = tangentBasis * halfVector;
spotDir = tangentBasis * gl_LightSource[1].spotDirection;
// pass texture coords to fragment shader ******************************************************
gl_TexCoord[0] = gl_MultiTexCoord0;
gl_TexCoord[1] = gl_MultiTexCoord1;
// putting distance in w component of light ****************************************************
lightDir = vec4(tempLight, 0.0);
lightDir.w = length(aux);
// convert vertex position *********************************************************************
gl_Position = ftransform();
}
Frag shader
varying vec4 lightDir;
varying vec3 normal, halfVector, spotDir;
uniform sampler2D tex;
uniform sampler2D norm;
void main()
{
vec3 n,l,halfV;
vec4 texel;
float NdotL,NdotHV;
float att;
float spotEffect;
float dist;
// retrieve material parameters ***************************************************************
vec4 color = gl_FrontLightModelProduct.sceneColor;
vec4 ambient = gl_FrontLightProduct[1].ambient;
vec4 diffuse = gl_FrontLightProduct[1].diffuse;
vec4 specular = gl_FrontLightProduct[1].specular;
// compute normals from normal map ************************************************************
vec2 tuv = vec2(gl_TexCoord[1].s, gl_TexCoord[1].t);
n = 2.0 * (texture2D(norm, tuv).rgb - 0.5);
n = normalize(n);
//n = normalize(normal);
// compute light ******************************************************************************
l = normalize(lightDir.xyz);
dist = lightDir.w;
NdotL = max(dot(n,l),0.0);
if (NdotL > 0.0)
{
spotEffect = dot(normalize(spotDir), normalize(-l));
if (spotEffect > gl_LightSource[1].spotCosCutoff)
{
spotEffect = pow(spotEffect, gl_LightSource[1].spotExponent);
att = spotEffect / (gl_LightSource[1].constantAttenuation +
gl_LightSource[1].linearAttenuation * dist +
gl_LightSource[1].quadraticAttenuation * dist * dist);
color += att * (diffuse * NdotL + ambient);
halfV = normalize(halfVector);
NdotHV = max(dot(n,halfV),0.0);
color += att * specular * pow(NdotHV, gl_FrontMaterial.shininess);
}
}
// apply texture ******************************************************************************
texel = texture2D(tex,gl_TexCoord[0].st);
color *= texel;
// set fragment color *************************************************************************
gl_FragColor = color;
}
The result is that the entire object is tinted blue; there is no bump; on a cube object, only 2 faces of the six light up, and before those two faces light up, there is a flicker or black snow when it gets to a certain angle. Other objects also light wierdly. Here are some examples:
note: the light stays in the same postion of (-3.0, 4.0, -5.0)
http://img.photobucket.com/albums/v296/dante4016/cube1.jpg
http://img.photobucket.com/albums/v296/dante4016/cube2.jpg
http://img.photobucket.com/albums/v296/dante4016/octo.jpg
http://img.photobucket.com/albums/v296/dante4016/sphere.jpg
The yellow and magenta bars you see are just from photoshop; don't know why it does that sometimes. One more thing is that when I don't multiple the bitangent by the handedness, the same thing happens except I don't get the black snow flicker, it just lights up the entire face in one frame or so. Being that the shader is from a tutorial, I think the problem lies in the tangent code (very sorry that it's a bit messy), but I'm not sure where. Any ideas what the problem could be?