[10 Dec 2012]

# Calculation of tangent and bitangent vectors

Normal mapping method perturbs normals of a model to add feel of a relief. To use this method it's required to somehow transform vectors from world space to tangent space. Such transformation can be made by rotation matrix that is formed from vertex normal, tangent and bitangent vectors. Normal is known, but we have to calculate tangent and bitangnet vectors. These vectors are perpendicular to the normal at the vertex, and have same direction as S and T texture coordinates gradient (direction of change) at the vertex.
We can calculate required vectors for every triangle in the mesh. The triangle consists of three vertices (A,B,C) with known position, normal and texture coordinates. For every point inside triangle we can write following:
$P-A=T(s_p-s_a)+B(t_p-t_a)$
where P - point inside triangle, T and B – tangent and bitangent vectors accordingly, sp,tp – texture coordinates of P, sa,ta – texture coordinates of vertex A. Right part of equality can be considered as positioning of point P in new coordinate space (actually in texture tangent space). T and B vectors define axes of the coordinate space, and differences of texture coordinates determine offsets along new axes.
We can offset center of coordinate system to vertex A. Positions and texture coordinates of vertices B and C in new coorditante system will be following:
$P_o=B-A$
$P_1=C-A$
$(s_0,t_0)=(s_b-s_a,t_b-t_a)$
$(s_1,t_1)=(s_c-s_a,t_c-t_a)$
And now vertices P0 and P1 can be expresed through their texture coordinates:
$P_0=T*s_0+B*t_0$
$P_1=T*s_1+B*t_1$
T and B vectors are unknown. This equations can be rewritten in matrix form:
$\begin{bmatrix} P_0_x & P_0_y & P_0_z \\ P_1_x & P_1_y & P_1_z \end{bmatrix} = \begin{bmatrix} s_0 & t_0 \\ s_1 & t_1 \end{bmatrix} * \begin{bmatrix} T_x & T_y & T_z \\ B_x & B_y & B_z \end{bmatrix}$
To calculate tangents and bitangents (to elimiante texture coordinates matrix on the right), you have to multiply left part of the equation by inverted texture coordinates matrix (which is with s and t components).
$Q=\frac{1.0}{s_0t_1-s_1t_0}$

$\begin{bmatrix} T_x & T_y & T_z \\ B_x & B_y & B_z \end{bmatrix} = Q * \begin{bmatrix} t_1 & -t_0 \\ -s_1 & s_0 \end{bmatrix} * \begin{bmatrix} P_0_x & P_0_y & P_0_z \\ P_1_x & P_1_y & P_1_z \end{bmatrix}$
Now we can iteratively calculate tangent and bitangent vectors for every triangle in the model. But normal mapping method requires these vectors to be defined for each vertex. As any vertex can be shared by multiple triangles, tangent and bitangent vectors of each vertex must be averaged to take into account all adjacent triangles:
 foreach vertex     vertex.T = 0; // tangent     vertex.B = 0; // binormal     foreach triangle         if (vertex in triangle) // test, for example, by vertex index             vertex.T += triangle.T             vertex.B += triangle.B     vertex.T = normalize(T)     vertex.B = noramlize(B) 
After calculation of tangent and bitangent vectors for every vertex, we can form tangent space to world space transfromation matrix:
$M_{tanToWorld}=\begin{bmatrix} T_x & B_x & N_x \\ T_y & B_y & N_y \\ T_z & B_z & N_z \end{bmatrix}$
And inverted matrix performs reverse transformation from world to tangent space. Inverse matrix of rotation matrix (matrix that contains orthonormal basis) is equal to transpose of rotation matrix. But previously calculated normal, tangent and bitangent vectors aren't necessarily orthogonal to each other (and transformation matrix isn't rotation matrix). T and B vectors can be orthogonalized with help of Gram-Shmidt process:
$T^I=T-N(N \cdot T)$
$B^I=B-N(N \cdot B)-T_I(T_i \cdot B)$
New vectors also require normalization. Now N, T and B vectors are orthonormal and can be used to form rotation matrix. Following matrix transforms from world to tangent space:
$M_{worldToTan}=\begin{bmatrix} T^I_x & T^I_y & T^I_z \\ B^I_x & B^I_y & B^I_z \\ N_x & N_y & N_z \end{bmatrix}$
This matrix is used in normal mapping method to transform vectors required for lighting from world to tangent space. Tangents and bitangents must be passed to shader as vertex attributes (per vertex data). Instead of passing six floating point values, we can pass only four FP values. Bitangent vector can be restored as cross product of normal and tangent vectors. Restored bitangnet vector must point in correct direction, but cross product of N and T can produce flipped bitangent. To restore directon of B, we pass one more value for each vector - H (handedness). H is equal to sign (1 or -1) of determinant of MworldToTan matrix.
$B=(N\times T) H$
New attribute to vertex shader has following layout: vec4, where xyz components store tangent vector, and w component - handedness value.
 in vec4 tangent; // xyz - tangent vector, w - handedness 

Sun and Black Cat- Igor Dykhta () © 2007-2014