Page 1 of 1

Tangent space is broken

Posted: Thu Mar 16, 2006 9:41 pm
by needforhint
I don't know wheather this is caused by my shader, but the shader displays normals and texture coordinates properly. but with tangetns or binormals I get very weird results. If this is really a bug, then it will be a very annoing fact about irrlicht making me so sad, I just hope it is the shader :cry:
see 4 yourself http://www.medeal.sk/irr/displaynormals.zip

here is what displayed normals looks like
Image
here is what displayed binormals looks like
Image

Posted: Tue Apr 11, 2006 3:02 pm
by Guest
I get the same thing, sapphire radeon 9600pro agp 8x.

Posted: Tue Apr 11, 2006 8:55 pm
by needforhint
OK, I just got an serious doubt about createMesheWithTangents() function, since the function doesn't only calculate tangets and bitangents but also changes the normals... see pic, the teapot on the left and room are tangent meshes, the teapot on the right is not tangetmesh,

theese are displayed normals, why has tangent mesh different normals? should it have?
Image

theese are displayed tangents, yes there is a difference
Image

so should normals of a mesh change after it becoms tangent mesh? I doubt that.

Posted: Tue Apr 11, 2006 9:14 pm
by Baal Cadar
Does the teapot have valid texture cooridnates? In order to generate valid tangent space bases, texture coordinates are needed, to properly align the tangent space with the normal map.

Posted: Tue Apr 11, 2006 9:45 pm
by needforhint
texture coordinates of both teapots are the same.. don't know wheather they are correct in a way you mean... the teapots have only 1 texture layer, no normal map

Posted: Tue Apr 11, 2006 9:53 pm
by Baal Cadar
Well, question is whether they have texture coordinates at all. That you have two teapots, doesn't matter here, as the createMeshWithTangents changes normals, as you said. And it also doesn't matter, that you have no normal map . If you don't have a normal map, then why do you create tangent space bases? They only make sense with some accordingly aligned normal map, or some other effect map, that is in tangent space.

So to properly rephrase my question: Does the teapot have texture coordinates defined? If they are all zero, then the result of createMesheWithTangents is undefined basically, which perfectly explains your results.

Posted: Tue Apr 11, 2006 10:20 pm
by needforhint
the teapots have texture coordinates defined, yes, the teapots were exported with their texture coordinates, you can display them as well in the shader and see how the texture is maped on the mesh... (did you run the program? it makes me ask)... the room has the the normal map aplied but the teapots don't. To be as objective as it could the thing is as follows:

room - a 3ds mesh with 2 texture layer(diffuse and normal) with texture coords, it is made a tangentMesh

teapot - an X mesh with 1 layer texture with texture coords, one of the teapots is made Tanget mesh, the second one is not

but there is no need to have a normal map layer on a mesh if you want to create a tangent space on it, you map the mesh's normals always, not normals in normal map.. thats why you dont need a normal map when you apply createMeshWithTangents() as well, ...

Posted: Fri Apr 21, 2006 7:52 pm
by needforhint
the problems remains with irrlicht 1.0, :cry:

Posted: Wed May 17, 2006 9:22 am
by xterminhate
I am currently developping a new createMeshWithTangent() that keeps original normals when calculating tangent and birnomal. Do you think that will solve the problem you have explained ?

Posted: Wed May 17, 2006 6:48 pm
by Electron
if you do that, make sure that the resulting TBN matrix still defines an orthagonal coordinate system

What I did in lightfeather was compute averaged (smooth) normals rather than just setting all vertices to the per-tri normal as irrlicht currently does

Posted: Tue May 23, 2006 12:45 pm
by chromdragon
Some problems I see, or maybe I am wrong:

1] If you smooth the tangent/bi tangent per vertex you don't lost the orthonormal basis? (no more 90)

2] How do I know what are the correct tangent/bi tangent when I use the NormalMaps generate my NVidia Tool (Melody) or ATI Normal Mapper?

3) I sow somewhere that I must flip the y (green) channel of the NormalMap (NVidai Melody)? Why is that?

So pls can anyone clarify me about this? (Normal mapping in ObjectSpace works OK but I don't need any tangent/bi tangent)

Is anyone who had correct calculated the normal/tangent/bitagent for TangetSpace? (normal-maps generated but ATI NormalMapper or NVIDIA Melody)

Computing tangents on GameDev

Posted: Tue May 23, 2006 7:50 pm
by Electron
I am NOT an expert on bump/normalmapping or tangent space, but I did struggle through doing the TBN matrix calculations for Lightfeather, so I'll post me code here in the hopes that it may be some help. I drew heavily on niko's code for the texture-gradient calculations, but the main difference in my code is that while I do recompute normals, they should be smooth normals. My grasp on the concepts is a tad tenuous though, so I make no guarantee that the following code is perfect. Also note that since this is Lightfeather code it is not directly compatible with Irrlicht. The concept, however, should transfer easily. Note that while I believe orthonality is maintained, I have not done any detailed analysis to confirm this.

Code: Select all

//! computes the tangents for a mesh buffer. If either parameter is false, that tangent will not
		//! be computed. 
		//! to make sure that the TBN matrix will have 3 mutually orthagonal axes,
		//! we need to recompute normals. It is possible that a solution could be devised
		//! which does not need to recompute normals, but this is easiest
		//! \param sTangent: create with s tangents (tangents)
		//! \param tTangent: create with t tangents (binormals)
	 	void CMeshBuffer::calculateTangents(bool sTangents, bool tTangents)
	 	{
			/* TODO (electron): Clean up this function and figure out what's going on, 
			                       what calculations are necessary We assume CW trianglewinding, which is 
										  rather bad form. The CCW winding code is commented out below
										  but untested*/
			
			
			//first we zero out all normals and tangents
			u32 cnt=vertexBuffer->getVertexCount();
			core::vector3df vecZero(0,0,0);
			for(u32 i = 0;i < cnt;i++)
			{
				vertexBuffer->setNormal(i,vecZero);
				if(sTangents)
				{
					vertexBuffer->setTangentS(i,vecZero);
				}
				if(tTangents)
				{
					vertexBuffer->setTangentT(i,vecZero);
				}
			}
			
			//we loop through all triangles and add
			//the normals and tangents for each triangle a vertex belongs to
			for(u32 i = 0;i < indexBuffer->getIndexCount() - 2;i += 3)
			{
				u32 idx0 = indexBuffer->getIndex32Safe(i);
				u32 idx1 = indexBuffer->getIndex32Safe(i + 1);
				u32 idx2 = indexBuffer->getIndex32Safe(i + 2);
				calculateTangentsForVertex(idx0,idx1,idx2,sTangents,tTangents);
				calculateTangentsForVertex(idx1,idx2,idx0,sTangents,tTangents);
				calculateTangentsForVertex(idx2,idx0,idx1,sTangents,tTangents);	
				//calculateTangentsForVertex(idx2,idx1,idx0,sTangents,tTangents);
				//calculateTangentsForVertex(idx1,idx0,idx2,sTangents,tTangents);
				//calculateTangentsForVertex(idx0,idx2,idx1,sTangents,tTangents);
			}
			//normalize all normals and tangents
			for(u32 i = 0;i < vertexBuffer->getVertexCount();i++)
			{
				core::vector3df vec=vertexBuffer->getNormal(i);
				vec.normalize();
				vertexBuffer->setNormal(i,vec);
				vec=vertexBuffer->getNormal(i);
				if(sTangents)
				{
					vec=vertexBuffer->getTangentS(i);
					vec.normalize();
					vertexBuffer->setTangentS(i,vec);
				}
				if(tTangents)
				{
					vec=vertexBuffer->getTangentT(i);
					vec.normalize();
					vertexBuffer->setTangentT(i,vec);
				}
			}
		}
		
		void CMeshBuffer::calculateTangentsForVertex(u32 idx0,u32 idx1, u32 idx2,bool sTangents, bool tTangents)
		{
			//i know the vertex and index buffer is slow here
			//with different types and data sizes this is the easiest and safest way, but it might
			//might be worthwhile to implement this a faster way
			//Irrlicht calcualtes matrix seperately for each evertex in a triangle
			//but should probably be safe to do it once per triangle. I'm sticking with
			//Irrlicht's way for now though
			core::vector3df vtx1=vertexBuffer->getPosition(idx0);
			core::vector3df vtx2=vertexBuffer->getPosition(idx1);
			core::vector3df vtx3=vertexBuffer->getPosition(idx2);
			core::position2d<f32> t1=vertexBuffer->getTextureCoord(idx0,0);
			core::position2d<f32> t2=vertexBuffer->getTextureCoord(idx1,0);
			core::position2d<f32> t3=vertexBuffer->getTextureCoord(idx2,0);
			//the following code is adapted from code in the 
			//Irrlicht engine by Nikolaus Gebhardt
			//and from code given at http://www.paulsprojects.net/tutorials/simplebump/simplebump.html
			core::vector3df v1 = vtx1 - vtx2;
			core::vector3df v2 = vtx3 - vtx1;
			core::vector3df normal = v2.crossProduct(v1);
			//I believe it would be better (in terms of smoothness) to normalize only at the end of adding in all
			//the normals from the different triangles a vertex is part of
			//but to retain orthagonality with the tangents
			//I believe it is imperative that we normalize here,
			//though its something to look into
			normal.normalize();
			
			
			// binormal (t tangent)
			f32 deltaX1 = t1.X - t2.X;
			f32 deltaX2 = t3.X - t1.X;
			core::vector3df tTangent = (v1 * deltaX2) - (v2 * deltaX1);
			tTangent.normalize();
			
			// tangent (s tangent)
			f32 deltaY1 = t1.Y - t2.Y;
			f32 deltaY2 = t3.Y - t1.Y;
			core::vector3df sTangent = (v1 * deltaY2) - (v2 * deltaY1);
			sTangent.normalize();
			
			// adjust
			core::vector3df txb = sTangent.crossProduct(tTangent);
			if (txb.dotProduct(normal) < 0.0f)
			{
				sTangent *= -1.0f;
				tTangent *= -1.0f;
			}
			
			vertexBuffer->setNormal(idx0,vertexBuffer->getNormal(idx0)+normal);
			if(sTangents)
			{
				vertexBuffer->setTangentS(idx0,vertexBuffer->getTangentS(idx0)+sTangent);
			}
			if(tTangents)
			{
				vertexBuffer->setTangentT(idx0,vertexBuffer->getTangentT(idx0)+tTangent);
			}
		}
[/code]

Posted: Wed May 24, 2006 11:47 am
by needforhint
bones animatedmeshes doesn't change their UV texure cords during animation! Thanks to this fact, the way how to have correct tangent space on animated meshes is to normalize tangent and bitangent (the "bitangent is a vector orthogonal to normal as well as to tangent so I think the correct name should be bitangentnormal :)") against the normal which changes during the animation, done in vertex shader.... I always had a problem of how to get theese UV axis (which get normalized to the normal and thus you get tangentU and bitangentV)for a smooth normal, becouse of faces list leading in the vertex and so. But your code helps a lot.
I am trying to achieve tangent space for animated meshes