How to Cast and Receive Shadows in Three.js
In Three.js, managing shadows is essential for creating realistic 3D scenes. This article provides a concise guide on how to explicitly configure individual 3D objects to either cast or receive shadows, covering renderer setup, light configuration, and specific object properties.
To enable and control shadows in Three.js, you must configure three distinct components: the renderer, the light source, and the individual 3D meshes.
Step 1: Enable Shadows in the Renderer
Before any object can project or display shadows, you must enable the shadow map in your WebGL renderer.
const renderer = new THREE.WebGLRenderer();
renderer.shadowMap.enabled = true;
// Optional: Choose shadow map type for better quality
renderer.shadowMap.type = THREE.PCFSoftShadowMap; Step 2: Configure the Light to Cast Shadows
Only specific light sources can cast shadows, such as
DirectionalLight, PointLight, and
SpotLight. You must explicitly set the light’s
castShadow property to true.
const light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(5, 10, 5);
light.castShadow = true; // Enable shadow casting for this light
scene.add(light);Step 3: Configure Individual Objects
Once the renderer and lights are configured, you can specify exactly
how each 3D mesh interacts with shadows using the
castShadow and receiveShadow boolean
properties.
To Make an Object Cast a Shadow:
If you want an object (like a character or a sphere) to block light
and project a shadow onto other surfaces, set its
castShadow property to true.
const sphereGeometry = new THREE.SphereGeometry(1, 32, 32);
const sphereMaterial = new THREE.MeshStandardMaterial({ color: 0xff0000 });
const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
sphere.castShadow = true; // This object will now cast a shadow
scene.add(sphere);To Make an Object Receive Shadows:
If you want an object (like a floor, wall, or table) to display
shadows cast by other objects, set its receiveShadow
property to true.
const floorGeometry = new THREE.PlaneGeometry(10, 10);
const floorMaterial = new THREE.MeshStandardMaterial({ color: 0x808080 });
const floor = new THREE.Mesh(floorGeometry, floorMaterial);
floor.rotation.x = -Math.PI / 2;
floor.receiveShadow = true; // This object will now show shadows cast upon it
scene.add(floor);Applying Properties to Loaded Models
When importing external 3D models (such as GLTF or OBJ files), you must traverse the model’s hierarchy to apply these properties to all child meshes.
loader.load('model.gltf', (gltf) => {
const model = gltf.scene;
model.traverse((node) => {
if (node.isMesh) {
node.castShadow = true;
node.receiveShadow = true;
}
});
scene.add(model);
});By explicitly setting castShadow and
receiveShadow on a per-object basis, you optimize rendering
performance by ensuring Three.js only calculates shadow maps for
necessary objects.