Computing tangent space basis vectors for an arbitrary mesh

The following code calculates the per-vertex tangent vectors for an arbitrary mesh. For mathematical details, see Mathematics for 3D Game Programming & Computer Graphics, Section 6.8.3.

The code generates a four-component tangent T in which the handedness of the local coordinate system is stored as ±1 in the w-coordinate. The bitangent vector B is then given by B = (N × T)Tw.

This code uses the Point2D class.
This code uses the Vector3D and Point3D classes.
This code uses the Vector4D class.

Help! The term binormal is commonly used as the name of the second tangent direction (that is perpendicular to the surface normal and s-aligned tangent direction). This is a misnomer. The term binormal pops up in the study of curves and completes what is known as a Frenet frame about a particular point on a curve. Curves have a single tangent direction and two orthogonal normal directions, hence the terms normal and binormal. When discussing a coordinate frame at a point on a surface, there is one normal direction and two tangent directions, which should be called the tangent and bitangent. Somewhere along the line, somebody used the wrong term and it caught on. Please help remedy the botched terminology by using the word bitangent instead of binormal in the context of bump mapping, etc.

#include "Vector4D.h"


struct Triangle
{
    unsigned short  index[3];
};


void CalculateTangentArray(long vertexCount, const Point3D *vertex, const Vector3D *normal,
        const Point2D *texcoord, long triangleCount, const Triangle *triangle, Vector4D *tangent)
{
    Vector3D *tan1 = new Vector3D[vertexCount * 2];
    Vector3D *tan2 = tan1 + vertexCount;
    ClearMemory(tan1, vertexCount * sizeof(Vector3D) * 2);
    
    for (long a = 0; a < triangleCount; a++)
    {
        long i1 = triangle->index[0];
        long i2 = triangle->index[1];
        long i3 = triangle->index[2];
        
        const Point3D& v1 = vertex[i1];
        const Point3D& v2 = vertex[i2];
        const Point3D& v3 = vertex[i3];
        
        const Point2D& w1 = texcoord[i1];
        const Point2D& w2 = texcoord[i2];
        const Point2D& w3 = texcoord[i3];
        
        float x1 = v2.x - v1.x;
        float x2 = v3.x - v1.x;
        float y1 = v2.y - v1.y;
        float y2 = v3.y - v1.y;
        float z1 = v2.z - v1.z;
        float z2 = v3.z - v1.z;
        
        float s1 = w2.x - w1.x;
        float s2 = w3.x - w1.x;
        float t1 = w2.y - w1.y;
        float t2 = w3.y - w1.y;
        
        float r = 1.0F / (s1 * t2 - s2 * t1);
        Vector3D sdir((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r,
                (t2 * z1 - t1 * z2) * r);
        Vector3D tdir((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r,
                (s1 * z2 - s2 * z1) * r);
        
        tan1[i1] += sdir;
        tan1[i2] += sdir;
        tan1[i3] += sdir;
        
        tan2[i1] += tdir;
        tan2[i2] += tdir;
        tan2[i3] += tdir;
        
        triangle++;
    }
    
    for (long a = 0; a < vertexCount; a++)
    {
        const Vector3D& n = normal[a];
        const Vector3D& t = tan1[a];
        
        // Gram-Schmidt orthogonalize
        tangent[a] = (t - n * Dot(n, t)).Normalize();
        
        // Calculate handedness
        tangent[a].w = (Dot(Cross(n, t), tan2[a]) < 0.0F) ? -1.0F : 1.0F;
    }
    
    delete[] tan1;
}

Public Source Code

The following are links to C++ snippets that we've released for various algorithms and techniques pertinent to 3D game programming.

Books by Eric Lengyel
Book

Mathematics for 3D Game Programming & Computer Graphics, Second Edition

This best-selling book teaches the mathematics and rendering techniques that a game programmer needs to develop a professional-quality 3D engine.
Book

The OpenGL Extensions Guide

This is the ultimate extension reference for the serious OpenGL programmer.
Books See all publications by Eric Lengyel

Conference Slides and Articles
GDC07

Projection Matrix Tricks (2007)
(PowerPoint, 3.26 MB)

This presentation examines the inner workings of the perspective projection matrix and discusses several techniques for modifying the properties of the projection matrix to solve specific rendering problems at zero cost.
GDC06

Advanced Light and Shadow Culling Methods (2006)
(PowerPoint, 852 kB)

This presentation focuses primarily on portal systems and describes algorithms and optimizations that can be applied to a graphics engine supporting completely dynamic lighting and shadows.
GDC05

Advanced Stencil Shadow and Penumbral Wedge Rendering (2005)
(PowerPoint, 1.00 MB)

This presentation reviews advanced implementation techniques of the stencil shadow algorithm and focuses on the relatively new method of penumbral wedge rendering used to generate soft-edged shadows.
Gamasutra

The Mechanics of Robust
Stencil Shadows

This article describes the mathematical details of efficient and robust stencil shadow rendering. These techniques are implemented in the C4 Engine.