Three.js Cel Shading with MeshToonMaterial
In Three.js, achieving a stylized, hand-drawn, or comic book
aesthetic relies heavily on MeshToonMaterial. This article
explores how MeshToonMaterial works, how it implements
cel-shading through light banding, and how developers can customize its
gradients and outlines to create a distinct 2D cartoon look in a 3D web
environment.
The Mechanics of Cel Shading
Traditional 3D shading materials, like MeshPhongMaterial
or MeshStandardMaterial, calculate smooth, continuous
gradients of light and shadow across a surface. This realistic
interpolation simulates how light behaves on physical objects.
MeshToonMaterial alters this behavior by breaking down
the smooth transition of light into discrete steps, a process known as
color quantization or banding. Instead of a soft gradient from bright to
dark, the material renders distinct, hard-edged regions of solid color.
This mimics the traditional hand-painted animation style (cel shading)
where artists use a limited palette of colors to represent light and
shadow.
The Role of Gradient Maps
The primary way to control the look of MeshToonMaterial
is through a gradient map. By default, Three.js applies
a default two-tone shading step (one level for shadow, one for light).
However, developers can define custom shading steps using a small, 1D
texture map passed to the gradientMap property.
To create custom steps, you define a texture containing the exact color bands you want. The critical step is configuring the texture filtering:
const colors = new Uint8Array([0, 127, 255]); // Three shading steps
const gradientMap = new THREE.DataTexture(colors, 3, 1, THREE.RedFormat);
// Prevent Three.js from smoothing the transitions
gradientMap.minFilter = THREE.NearestFilter;
gradientMap.magFilter = THREE.NearestFilter;
gradientMap.needsUpdate = true;
const material = new THREE.MeshToonMaterial({
color: 0x3f51b5,
gradientMap: gradientMap
});Using THREE.NearestFilter is essential. Without it, the
GPU will linearly interpolate between the pixels of your gradient map,
smoothing out the edges and defeating the purpose of the toon
effect.
Achieving the Comic Book Aesthetic with Outlines
While MeshToonMaterial handles the internal shading of a
3D model, a true comic book aesthetic requires dark outlines to define
the edges of the object. Three.js does not automatically generate these
outlines through the material itself, but they can be achieved using two
common techniques:
1. The Inverted Hull Method
This technique involves rendering a second, slightly larger version of the mesh behind the original. This outer mesh is rendered with its front faces culled (hidden) and colored solid black. The result is a clean, consistent outline around the object that responds dynamically to camera movement.
2. Post-Processing Outlines
For more complex scenes, post-processing is highly effective. By
utilizing EffectComposer alongside shaders like the
OutlinePass or Sobel/Lapacian edge detection filters,
developers can detect depth and normal differences in the rendered image
and draw black lines directly onto the 2D screen space. This allows for
detailed line-art styles that cover both the outer contours and the
inner crevices of 3D models.