Up until now, our 3D scenes might feel a little flat, even with lighting. The objects cast no shadows, making it hard to perceive their depth and relationship to the environment. Shadows are a fundamental aspect of how we perceive the real world, and their absence in a 3D scene can make it look artificial. In this section, we'll introduce the concept of shadows in Three.js and learn how to implement them to add a crucial layer of realism to your creations.
The core idea behind shadows in 3D rendering is that certain objects block light, preventing it from reaching other surfaces. Three.js simulates this by using a technique called shadow mapping. Essentially, the light source renders the scene from its own perspective, creating a depth map. This depth map, known as a shadow map, is then used to determine which parts of the scene are in shadow when rendered from the camera's perspective.
To enable shadows in your Three.js scene, you need to make a few key configurations. First, you must enable shadows on the renderer. Then, you need to tell each light that casts shadows to do so, and importantly, mark each object that should receive and cast shadows.
const renderer = new THREE.WebGLRenderer();
renderer.shadowMap.enabled = true;Not all lights can cast shadows. Typically, you'll want to use DirectionalLight or SpotLight for shadow casting. Point lights can also cast shadows, but they require a bit more setup. When you create a shadow-casting light, you need to enable its shadow properties.
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.set(5, 5, 5);
directionalLight.castShadow = true;For an object to participate in the shadow casting process, it needs to be marked as such. This involves setting two properties: castShadow to true for objects that will block light, and receiveShadow to true for objects that will have shadows drawn on them.
const cube = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1), new THREE.MeshStandardMaterial({ color: 0x00ff00 }));
cube.castShadow = true;
cube.receiveShadow = true;
scene.add(cube);