Tangent space is broken

You discovered a bug in the engine, and you are sure that it is not a problem of your code? Just post it in here. Please read the bug posting guidelines first.
Post Reply
needforhint
Posts: 322
Joined: Tue Aug 30, 2005 10:34 am
Location: slovakia

Tangent space is broken

Post 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
what is this thing...
Guest

Post by Guest »

I get the same thing, sapphire radeon 9600pro agp 8x.
needforhint
Posts: 322
Joined: Tue Aug 30, 2005 10:34 am
Location: slovakia

Post 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.
what is this thing...
Baal Cadar
Posts: 377
Joined: Fri Oct 28, 2005 10:28 am
Contact:

Post 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.
needforhint
Posts: 322
Joined: Tue Aug 30, 2005 10:34 am
Location: slovakia

Post 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
what is this thing...
Baal Cadar
Posts: 377
Joined: Fri Oct 28, 2005 10:28 am
Contact:

Post 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.
needforhint
Posts: 322
Joined: Tue Aug 30, 2005 10:34 am
Location: slovakia

Post 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, ...
what is this thing...
needforhint
Posts: 322
Joined: Tue Aug 30, 2005 10:34 am
Location: slovakia

Post by needforhint »

the problems remains with irrlicht 1.0, :cry:
what is this thing...
xterminhate
Posts: 206
Joined: Thu Sep 01, 2005 9:26 pm
Location: France

Post 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 ?
Return to Irrlicht after years... I'm lovin it.
It's hard to be a Man !
Si vis pacem para belum
Electron
Posts: 874
Joined: Sun Mar 14, 2004 12:05 am
Location: Massachusetts USA

Post 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
You do a lot of programming? Really? I try to get some in, but the debugging keeps me pretty busy.

Crucible of Stars
chromdragon
Posts: 101
Joined: Wed Feb 15, 2006 4:22 pm
Location: RO

Post 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
Electron
Posts: 874
Joined: Sun Mar 14, 2004 12:05 am
Location: Massachusetts USA

Post 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]
You do a lot of programming? Really? I try to get some in, but the debugging keeps me pretty busy.

Crucible of Stars
needforhint
Posts: 322
Joined: Tue Aug 30, 2005 10:34 am
Location: slovakia

Post 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
what is this thing...
Post Reply