Understanding Morph Normals in Three.js Lighting

This article explains what morph normals are and why they are essential for rendering realistic lighting on morph target animations in Three.js. You will learn how vertex normals change during shape deformation and why updating these normals is critical to preventing static, broken, or unrealistic shadows and highlights on your animated 3D models.

What are Morph Targets and Normals?

To understand morph normals, it helps to first understand the two components they unite: morph targets and vertex normals.

What are Morph Normals?

Morph normals are the corresponding normal vectors for a morph target’s deformed state. Just as a morph target stores the new positions of the vertices for a deformed shape, it must also store the new directions of the normals for that same shape.

When a 3D model morphs from a neutral face to a smiling face, the vertices move outward and upward. Because the physical shape of the surface has changed, the angles of the surface faces change as well. Morph normals represent these new surface angles.

Why Morph Normals are Necessary for Accurate Lighting

Without morph normals, lighting calculations on an animated mesh will fail to render realistically.

If you animate a morph target’s positions but do not update its normals, Three.js will calculate lighting using the vertex normals of the original, undeformed base mesh. As the geometry deforms, the light and shadows will remain frozen in the original shape’s state.

This mismatch creates several visual issues: * Inaccurate Shading: A cheek that puffs out during a smile will still be shaded as if it were flat, making the deformation look unnatural and lose its three-dimensional depth. * Static Highlights: Specular highlights (the shiny spots on a material) will not move dynamically across the surface as the mesh deforms. * Broken Realism: The lack of dynamic shadow and highlight transitions breaks the illusion of organic movement, making the animation look flat and artificial.

By utilizing morph normals, Three.js can linearly interpolate both the vertex positions and the vertex normals simultaneously. As the mesh deforms, the shader calculates lighting based on the actively morphing normals. This ensures that shadows, diffuse lighting, and specular highlights realistically wrap around, bend, and react to the changing contours of the animated 3D model in real time.

Implementation in Three.js

When exporting 3D models (such as .gltf or .glb files) from software like Blender, morph normals are typically exported automatically alongside the morph target positions.

In Three.js, standard built-in materials—such as MeshStandardMaterial or MeshPhongMaterial—automatically detect and apply morph normals during rendering. To ensure they are active, the vertex shader handles the interpolation on the GPU, allowing for high-performance, dynamically lit morph animations without sacrificing frame rates.