Why glTF Tangents Store Bitangent Sign in W
In the glTF specification, the tangent attribute is defined as a
four-component vector (VEC4), where the first three
components represent the XYZ tangent direction and the fourth component
(w) is a sign indicator (+1.0 or -1.0). This article
explains why the bitangent vector is represented this way, focusing on
memory optimization, runtime derivation through the cross product, and
the necessity of handling mirrored UV mapping coordinates.
Memory and Bandwidth Optimization
To construct a tangent-space basis (the Tangent, Bitangent, Normal, or TBN matrix) for normal mapping, a vertex shader requires three orthogonal vectors: the normal (\(\mathbf{N}\)), the tangent (\(\mathbf{T}\)), and the bitangent (\(\mathbf{B}\)).
Storing the bitangent as a separate three-component vector
(vec3) would require an additional 12 bytes of data per
vertex using standard 32-bit floats. Because the tangent space vectors
are mutually perpendicular, the bitangent can be computed at runtime
rather than stored explicitly. Packing the sign of the bitangent into
the fourth component (\(w\)) of the
tangent vector expands the tangent from a vec3 to a
vec4, adding only 4 bytes (or fewer if quantized) instead
of the 12 bytes needed for a full vector. This reduction saves storage
footprint and reduces GPU memory bandwidth.
Mathematical Reconstruction via the Cross Product
When the normal vector and tangent vector are known and normalized, their cross product yields a third vector that is perpendicular to both:
\[\mathbf{B} = (\mathbf{N} \times \mathbf{T}) \cdot w\]
The cross product inherently generates a right-handed orthogonal vector. However, texture mapping does not always follow a fixed right-handed coordinate system. Multiplying the result of the cross product by the \(w\) scalar (+1.0 or -1.0) dynamically flips or preserves the direction of the reconstructed bitangent vector to match the source mesh's UV orientation.
Preserving UV Handedness and Mirroring
The primary functional reason for the sign vector is handling chirality (handedness) resulting from mirrored UV coordinates.
In 3D asset workflows, artists frequently mirror UV maps across symmetrical geometry (such as characters' left and right sides) to maximize texture resolution and save texture space. When a UV island is mirrored, the coordinate space flips from a right-handed system to a left-handed system. Without the \(w\) sign component:
- The standard cross product \(\mathbf{N} \times \mathbf{T}\) would assume a fixed handedness.
- On mirrored geometry, the calculated bitangent would point in the opposite direction of the actual V-axis of the texture space.
- Normal maps would invert along the mirrored axis, making indentations appear as protrusions and distorting surface lighting.
Encoding \(+1.0\) for right-handed tangent spaces and \(-1.0\) for left-handed (mirrored) tangent spaces ensures the fragment shader constructs the correct TBN matrix regardless of how the UV coordinates are aligned or transformed.