Configure Realistic SpotLight Shadows in Three.js
Setting up realistic shadows with a SpotLight in
Three.js requires configuring the renderer, the light itself, the shadow
camera, and the objects in the scene. This guide provides a step-by-step
walkthrough to enable shadow maps, adjust spotlight properties,
fine-tune shadow camera settings, and resolve common rendering issues
like shadow acne for a visually stunning 3D scene.
Step 1: Enable Shadows in the Renderer
Before any light can cast a shadow, you must explicitly instruct the WebGL renderer to support shadow maps. You should also select a shadow map type that supports soft edges for a more realistic appearance.
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap; // Enables high-quality soft shadows
document.body.appendChild(renderer.domElement);Step 2: Configure the SpotLight
Create the SpotLight and set its castShadow
property to true. You should also adjust the
angle and penumbra properties. The
penumbra property defines the blurriness of the shadow
edges, which is crucial for realism.
const spotLight = new THREE.SpotLight(0xffffff, 5);
spotLight.position.set(15, 40, 15);
spotLight.angle = Math.PI / 6; // Cone width of the spotlight
spotLight.penumbra = 0.8; // Smooth transition at the edges (0 to 1)
spotLight.castShadow = true; // Enable shadow casting
scene.add(spotLight);Step 3: Optimize Shadow Map Resolution and Bias
By default, Three.js uses low-resolution shadow maps, resulting in
blocky, pixelated shadows. Increasing the shadow map size improves
resolution. Additionally, adjusting the shadow bias
prevents “shadow acne”—an artifact where pixel patterns render
incorrectly on surfaces.
// Default is 512x512. Higher values (1024, 2048) yield sharper shadows.
spotLight.shadow.mapSize.width = 2048;
spotLight.shadow.mapSize.height = 2048;
// Minor offset to prevent self-shadowing artifacts
spotLight.shadow.bias = -0.0001; Step 4: Fine-tune the Shadow Camera
A SpotLight uses a perspective camera under the hood to
calculate shadows. Restricting this camera’s frustum (near and far
clipping planes) to fit your scene precisely saves memory and improves
shadow quality.
spotLight.shadow.camera.near = 10; // Start calculating shadows at this distance
spotLight.shadow.camera.far = 100; // Stop calculating shadows past this distance
spotLight.shadow.camera.fov = 30; // Matches the spotlight's cone angleStep 5: Configure Scene Objects to Cast and Receive Shadows
For shadows to display, you must define which 3D objects cast shadows and which surfaces receive them.
// Example: A mesh that casts a shadow
const geometry = new THREE.BoxGeometry(5, 5, 5);
const material = new THREE.MeshStandardMaterial({ color: 0xff0000 });
const cube = new THREE.Mesh(geometry, material);
cube.castShadow = true; // This object will block light
cube.receiveShadow = true; // This object will display shadows cast onto it
scene.add(cube);
// Example: A floor that receives the shadow
const floorGeometry = new THREE.PlaneGeometry(100, 100);
const floorMaterial = new THREE.MeshStandardMaterial({ color: 0x808080 });
const floor = new THREE.Mesh(floorGeometry, floorMaterial);
floor.rotation.x = -Math.PI / 2;
floor.receiveShadow = true; // Shadows will be projected here
scene.add(floor);