Home Products Download Order Contacts


Subject: Re: tangent space for normal mapping

"halex2000" wrote in message
> I need to create the inverse TBN matrix for normal mapping in Shaders, but
> I'm confused about the math. Could someone help me? (note that my math
> skills sucks)
> -My mesh has averaged normal among shared vertices. I calculate tangent
> and bitangent correctly for triangle normal, but now I need to make them
> orthogonal. Can I simply do as I did for normals, that is average the
> tangent and bitangent among shared vertices? Will the three vectors be
> orthogonal?
> -When I have the TBN matrix I need to invert it. I've read that since TBN
> is orthonormal, I can simplly transpose it. Is this correct?

You asked this in comp.graphics.api.opengl and, apparently, in
the forums at gamedev.net. It is admirable that you have such
enthusiasm for wanting to work with computer graphics and
shaders. I must say, though, that every time the question is
posed--"I want to know about the math topic FooBar, but my
math skills suck"--I have to wonder if the poster's time is better
spent learning the math skills first before attempting programming
that requires those skills.

As I had mentioned in the opengl newsgroup, one of your goals
is to produce a set of vertex tangents that correspond to a
"continuous" tangent vector field over the surface that your mesh
represents. A standard approach is to think of the vertices as
samples from a smooth parametric surface P(u,v), where (u,v)
are texture coordinates for the texture to which you will apply the
normal mapping. The partial derivative T = dP/du is a tangent
vector to the surface at a vertex. If N is a *surface* normal,
choose the bitangent to be B = Cross(N,T). I had supplied a link
to a calculus-based explanation for computing dP/du at a vertex
of a triangle in the mesh,
The end result is an equation that is relatively easy to implement.

The problems in practice are (1) you have a unit-length *vertex*
normal N', which is not necessarily a surface normal relative to the
parameterization by (u,v), and (2) this algorithm produces a
vector T that is not necessarily unit length or perpendicular
to N'. You project out the N' component in T and then normalize
the result,
T = T - Dot(T,N')*N'
T' = T/Length(T)
Now you have unit-length vectors N' and T' that are perpendicular.
Choose B' = Cross(N',T'), another unit-length vector perpendicular
to N' and T'.

Here is an implementation of the algorithm for producing T for a
triangle with vertex positions P0, P1, P2 and corresponding texture
coordinates (u0,v0), (u1,v1), and (u2,v2). The projection and
normalization steps are easy enough to apply to the output of
this function.

bool SimpleBumpMapEffect::ComputeTangent (const Vector3f& rkPos0,
const Vector2f& rkTCoord0, const Vector3f& rkPos1,
const Vector2f& rkTCoord1, const Vector3f& rkPos2,
const Vector2f& rkTCoord2, Vector3f& rkTangent)
// Compute the change in positions at the vertex P0.
Vector3f kDP1 = rkPos1 - rkPos0;
Vector3f kDP2 = rkPos2 - rkPos0;

if (Mathf::FAbs(kDP1.Length()) < Mathf::ZERO_TOLERANCE
|| Mathf::FAbs(kDP2.Length()) < Mathf::ZERO_TOLERANCE)
// The triangle is very small, call it degenerate.
return false;

// Compute the change in texture coordinates at the vertex P0 in the
// direction of edge P1-P0.
float fDU1 = rkTCoord1[0] - rkTCoord0[0];
float fDV1 = rkTCoord1[1] - rkTCoord0[1];
if (Mathf::FAbs(fDV1) < Mathf::ZERO_TOLERANCE)
// The triangle effectively has no variation in the v texture
// coordinate.
if (Mathf::FAbs(fDU1) < Mathf::ZERO_TOLERANCE)
// The triangle effectively has no variation in the u
// Since the texture coordinates do not vary on this triangle,
// treat it as a degenerate parametric surface.
return false;

// The variation is effectively all in u, so set the tangent vector
// to be T = dP/du.
rkTangent = kDP1/fDU1;
return true;

// Compute the change in texture coordinates at the vertex P0 in the
// direction of edge P2-P0.
float fDU2 = rkTCoord2[0] - rkTCoord0[0];
float fDV2 = rkTCoord2[1] - rkTCoord0[1];
float fDet = fDV1*fDU2 - fDV2*fDU1;
if (Mathf::FAbs(fDet) < Mathf::ZERO_TOLERANCE)
// The triangle vertices are collinear in parameter space, so treat
// this as a degenerate parametric surface.
return false;

// The triangle vertices are not collinear in parameter space, so choose
// the tangent to be dP/du = (dv1*dP2-dv2*dP1)/(dv1*du2-dv2*du1)
rkTangent = (fDV1*kDP2-fDV2*kDP1)/fDet;
return true;

Dave Eberly


View All Messages in comp.graphics.algorithms

tangent space for normal mapping =>

Re: tangent space for normal mapping

Copyright 2006 WatermarkFactory.com. All Rights Reserved.