Shadow mapping is the most common and versatile technique for rendering realistic shadows in 3D graphics, including Three.js. It works by simulating how light behaves: what parts of the scene are occluded from the light source. Let's break down how it functions and the key considerations for achieving convincing results.
The core idea behind shadow mapping is to render the scene from the perspective of the light source. This creates a 'depth map' where each pixel stores the distance from the light to the nearest object. When rendering the scene from the camera's perspective, for each pixel, we compare its depth to the depth stored in the shadow map. If the pixel's depth is greater than the stored depth, it means that pixel is in shadow.
graph TD
A[Light Source] --> B{Render Scene from Light's POV};
B --> C[Shadow Map (Depth Texture)];
D[Camera] --> E{Render Scene from Camera's POV};
E --> F[Fragment Shader];
F -- Compare Fragment Depth --> C;
C -- Depth Check --> F;
F -- Determine Shadow or Not --> G[Final Pixel Color];
In Three.js, this process involves several steps. First, you need a light that can cast shadows, typically a DirectionalLight or SpotLight. You then need to enable shadow casting on that light and on the objects that should cast and receive shadows. Finally, you enable shadow mapping on the WebGLRenderer.
const light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(10, 10, 10);
light.castShadow = true;
scene.add(light);
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshStandardMaterial({ color: 0xff0000 });
const cube = new THREE.Mesh(geometry, material);
cube.castShadow = true;
cube.receiveShadow = true;
scene.add(cube);
renderer.shadowMap.enabled = true;A crucial parameter is the shadow map's resolution. A higher resolution means a more detailed shadow map, leading to sharper shadows. However, this comes at the cost of increased memory usage and processing time. Too low a resolution can result in 'aliasing' or 'jagged' shadows.
Another common issue is 'peter panning', where shadows appear to 'float' slightly above the surface they should be attached to. This happens when the shadow map's bias is not set correctly. You can adjust the shadow.bias property on the light to mitigate this.