How to calculate TBN matrix

Researching per vertex TBN matrix has uncovered three completely different and I would have thought, incompatible, algorithms. Looking through this site I can find no definitive explanation of the subject so I was hoping this could be rectified.

The different algorithms all calculate the edges the same. Namely, of a triangle with the vertices v1, v2, and v3:

Vec3 v2v1=v2.xyz - v1.xyz;
Vec3 v3v1=v3.xyz - v1.xyz ;

And the tex coords :

Vec2 c2c1=c2.xy - c1.xy ;
Vec2 c3c1=c3.xy – c1.xy ;

They also calculate a common scale factor, whose purpose I don’t quite understand and have not found explained :

Float scaleFactor=c2c1.x * c3c1.y – c3c1.x * c2c1.y;
If(scaleFactor==0)
	Use identity matrix for TBN
Else
	scaleFactor=1.0/scaleFactor;

The disparity comes when calculating the tangent and bitangent vectors. The three versions are:

T=(c3c1.y * v2v1.xyz - c2c1.y  * v3v1.xyz) * scaleFactor ;
B=(-c3c1.x * v2v1.xyz – c2c1.x * v3v1.xyz) * scaleFactor ;

T=(-c3c1.y * v2v1.xyz – c2c1.y * v3v1.xyz) * scaleFactor ;
B=(-c3c1.x * v2v1.xyz – c2c1.x * v3v1.xyz) * scaleFactor ;
T=(c3c1.y * v2v1.xyz – c2c1.y * v3v1.xyz) * scaleFactor ;
B=(c2c1.x * v3v1.xyz – c3c1.x * v2v1.xyz) * scaleFactor ;

It would be useful if this forum contained a definitive ruling on which of three versions is correct for openGL?

I have also come across a rather involved method for inverting the TBN matrix so light vector and eye vector can be transformed into tangent space. Could someone throw an eye over it and verify that it is right or even needed…

Float inv=(T.x * B.y * N.z – T.z * B.y * N.x) 
+ (B.x * N.y * T.z – B.z * N.y * T.x) + (N.x * T.y * B.z – N.z * T.y * B.x);

T.x=Bcross(N).x * inv;
T.y=-Ncross(T).x * inv;
T.z=T cross(B).x * inv;
Normalize(T);

B.x= -Bcross(N).y * inv;
B.y= Ncross(T).y * inv;
B.z=-Tcross(B).y * inv;
Normalize(B);

N.x=Bcross(N).z * inv;
N.y=-Ncross(T).z * inv;
N.z=Tcross(B).z * inv ;
Normalize(N);

Thanks in advance.

Different engines define the TBN directions differently - I’m sure you’ll have heard a 3D artist mention “flipping the green channel” or something along those lines. There’s no right or wrong answer, pick the one that’s most convenient to you and stick with it.

As for that last snippet, I always thought it best to build a surface normal by performing two matrix multiplies - the unpacked normal sample with the TBN matrix which would have been multiplied by the mesh’s world matrix in the vertex shader. Not sure why you’d want to do that.

Thanks NeXEkho. Do we know then, what method photoshop and GIMP use to store normals in normal maps? Presumably whichever TBN matrix one chooses must be coordinated with whichever method is used to store normals in a normal map?

The explanation that came with the second snippet was that it was used to invert the TBN matrix so it could be used to transform light vec etc into tangent space, whereas the straight TBN matrix as calculated actually transforms from tangent space into object space. I assumed it was a way of providing separate vectors that could be used instead of the full inverted matrix. You seem to be saying the code snippet is performing the same task as the Gram Scmidt orthoganilization algorithm.

Cards up front, I am not a mathematician, I am just trying to get together the tools I need to progress with the game build.

The Gamasutra website has a completely different take on creating TBN which seems the most comprehensible of the lot:

  1. calculate the edges as above

  2. calculate the face normal by crossing v3v1.xyz with v2v1.xyz. Normalize. Their website is actually: cross(v2v1.xyz, v3v1.xyz), but their article assumes Dx which is left handed. I think I am right to reverse the crossing order to turn normals to the openGL side.

  3. Given that T is the change of xyz in world units with respect to u

if(c2c1.u != 0):
T= v2v1.xyz / c2c1.u
else
T= v3v1.xyz / c3c1.u

normalize T

  1. B = cross( T, N)

  2. Assign each vertex of the face with the TBN.

for each vertex:
if the vertex has more than one TBN value (partakes of more than one face):
T = (T1 + T2 + Tn)/n.normalize()
B = (B1 + B2 + Bn)/n.normalize()
N = (N1 + N2 + Nn)/n.normalize()

Any thoughts on this?

An excellent tutorial with working C code is available at the following address:

http://www.terathon.com/code/tangent.html