Role of Matrix3 for Normals in Three.js

In Three.js, the Matrix3 class is primarily used to handle the transformation of normal vectors, ensuring they remain perpendicular to a 3D model’s surface during rendering. While 3D positions are transformed using a 4x4 matrix (Matrix4), normal vectors represent directions and are highly sensitive to non-uniform scaling. This article explains why Matrix3 is required to calculate the “normal matrix,” how it prevents visual distortion, and how it is implemented in WebGL shaders.

Understanding the Problem: Why Matrix4 Fails Normals

When a 3D model is translated, rotated, or scaled, its vertex positions are transformed using a 4x4 model-view matrix. However, applying this same matrix to normal vectors (which define the perpendicular direction of a surface) causes issues:

The Solution: The Normal Matrix

To transform normals correctly, WebGL requires a specific 3-by-3 matrix called the normal matrix. The normal matrix is mathematically defined as the inverse transpose of the upper-left 3x3 portion of the model-view matrix.

Using the inverse transpose mathematically cancels out the stretching effect of non-uniform scaling while correctly applying any rotation. Because translation is not needed for direction vectors, a 3x3 matrix (Matrix3) is the mathematically efficient and correct choice to store this transformation.

How Three.js Utilizes Matrix3

Three.js automates this complex math using the Matrix3 class. When rendering a scene, Three.js calculates the normal matrix for each mesh and passes it to the GPU.

If you are writing custom shaders or need to calculate this manually, Three.js provides a built-in method to convert a Matrix4 into the correct Matrix3 normal matrix:

const normalMatrix = new THREE.Matrix3();
normalMatrix.getNormalMatrix( mesh.modelViewMatrix );

The .getNormalMatrix() method automatically extracts the 3x3 rotation and scale components, calculates the inverse, transposes it, and stores the result in your Matrix3 instance.

Role in Shaders (GLSL)

In a WebGL vertex shader, the Matrix3 normal matrix is passed as a uniform variable of type mat3 (named normalMatrix). The shader uses it to transform the vertex normals from model space into view space before lighting calculations are performed:

uniform mat3 normalMatrix;
attribute vec3 normal;

void main() {
    // Transform the normal vector to view space
    vec3 transformedNormal = normalize(normalMatrix * normal);
    
    // Continue with vertex positioning...
}

By utilizing Matrix3 to compute and pass the normal matrix, Three.js ensures that your 3D models reflect light accurately, regardless of how they are rotated or scaled in the 3D scene.