Quick Intro to Tangent Space – For 3D Artists
If you’re thinking “I’ve herd of Tangents before”, and you’ve not spent a lot of time animating or learning calculus, then there’s a good chance it’s from Tangent Space Normal Maps. In this short I’m going to take a quick look at tangent space, and relate it back to the Cross Product.
What is a Tangent?
A tangent by itself is usually represented as a straight line on a curve – normally on a graph – and represents the instantaneous rate of change at that point. For a more complete understanding of tangents, you’d need to spend some time learning introductory Calculus, which is not something I’d suggest many artists worry about unless there’s specific interest.

C
here is the tangent line of this graph, and the angle of it’s slope represents the rate of change. Steeper the slope, the faster the change.Tangents also apply to 3D meshes, and with tangent space mapping, the tangent is the direction or “flow” of a mesh, representing largest rate of change in an meshes U
co-ordinate in texture space.
I’m not going to go into computing the tangent space here, if you want to go down another rabbit hole, here’s a good thread from gamedev.stackexchange explaining directly. But you can think of the tangent
as a “forward axis” along a mesh, with the bitangent
being the “right” axis, and the normal
being the “up” axis.
MikkTSpace and Tangent Space Normal Maps
The tangent vector is helpful as it gives gives us one of the two base vectors for our tangent space mapping. If we have two perpendicular directions in a 3D space, we can use the Cross Product to calculate the third. It’s common practise, using the MikkTSpace
tangent conversion algorithm, covered in Morten Mikkelsen’s thesis here.
When encoding normal maps using Mikkelsen’s algorithm – which has almost become the standard for many 3D programs – only the normal
and the tangent
are stored. The bitangent
, sometimes also known as the binormal
, is computed from the Cross Product of the tangent
and the normal
when the mesh data is loaded by another software package.
The tangent
, bitangent
, and normal
are in essence the co-ordinate system of texture space, like the XYZ
of 3D space.
Tangent space normal maps adjust normals based on the surface tangent. Thus will look odd when used on a mesh with different surface tangents. This means that we cannot expect a normal map, baked with on mesh, to look right on a different mesh.

T
angent, B
itangent, and the N
ormal vectors of a surface.Take this small quad, comprised of two triangles. On each triangle we can see the N
or normal vector in green, pointing directly away from the triangle’s face. The T
or tangent vector is in blue, pointing to the top left in this image, and the B
or bitangent vector in red, pointing to the bottom left.
TBN
vectors just looked like our XYZ
gizmos we’d normally see in a 3D DCC package – I’ve coloured the vectors the same so it’s a little more familiar. However in the above animated clip you can see how the TBN
vectors move and re-orient with the animated plane.
Because the tangent represents the direction of the mesh at a given point, if the tangent is incorrect, the normals from the normal map will be pointing in a different direction, and thus odd shading can occur.

Tangent space normal maps store normals based upon the original tangents of the mesh. A normal of (0,1,0)
in a normal map doesn’t mean point up in world or object space, it means “Point directly away from the models surface”. So this tangent-space up vector would be an unchanged normal, as this is what the face normal would have been in the first place.
Once the normal map has been decoded, we have a unit vector per pixel, which in the fragment shader we’d transform into object space.

TBN Matrix
.The tangent
, bitangent
, normal
matrix, or TBN Matrix
is what is the end destination for the tangent space vectors we’ve been looking at. When these are assembled in a matrix, we can then multiply our decoded normal vector by this matrix. This transforms it, from the flat (tangent) space of our normal map, to the object space of our mesh. In this case specifically, it just rotates our normal vector to our mesh’s surface.
To Summarise
This short takes a quick fly-by of tangent space mapping as a supporting component of my Cross Product article. There’s much more to touch on, like linear transforms, but hopefully this provides the start of a foundation.