Sign in to follow this  
ace4016

Tangent problem

Recommended Posts

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?

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this