wikis / Three.js / wiki / concepts / shadows.md view as markdown report a mistake
Definition
Three.js casts shadows using shadow maps by default: for every shadow-casting light, every shadow-casting object in the scene is re-rendered from that light's point of view, and the resulting depth data is used at final-render time to decide which pixels of the scene are in shadow.
How It Works
Enabling shadows requires switching on three cooperating flags — the renderer, the light, and each mesh:
const renderer = new THREE.WebGLRenderer({antialias: true, canvas});
renderer.shadowMap.enabled = true;
const light = new THREE.DirectionalLight(color, intensity);
light.castShadow = true;
const groundMesh = new THREE.Mesh(planeGeo, planeMat);
groundMesh.receiveShadow = true; // ground only receives
const cubeMesh = new THREE.Mesh(cubeGeo, cubeMat);
cubeMesh.castShadow = true; // casts and receives
cubeMesh.receiveShadow = true;
The cost is per light, per shadow-casting object. With 20 shadow-casting objects and 5 shadow-casting lights, the scene is drawn 6 times per frame — once from each light's viewpoint to build its shadow map, plus once for the actual camera view. A single shadow-casting PointLight alone costs 6 extra scene draws, because it shines in all directions and its shadow is effectively 6 SpotLight shadows pointed at the faces of a surrounding cube. This is why production scenes commonly limit shadow-casting to one directional light rather than letting every light cast shadows, and why some apps precompute static lighting into lightmaps/ambient-occlusion maps, or fake shadows entirely with a soft round texture on a plane beneath an object instead of a real shadow map.
Each shadow-casting light type uses a different shadow camera:
DirectionalLightuses anOrthographicCamerawhoseleft/right/top/bottom/near/far/zoomyou set directly onlight.shadow.camera. Only what's inside that camera's box receives/casts shadows — visualize it withnew THREE.CameraHelper(light.shadow.camera).SpotLight's shadow camera is aPerspectiveCamerawhosefovis driven automatically by the light's ownangle, and whoseaspectis derived from the shadow map size — most of it is not manually tunable the way aDirectionalLight's is.PointLight's shadow camera only exposesnear/farsince the light shines in every direction; internally it renders 6 directions like aSpotLightshadow per cube face, which is why it is the most expensive of the three.
Shadow map resolution is a separate control from shadow camera area. The shadow camera's box is stretched across a fixed-size texture (light.shadow.mapSize.width/.height, default 512x512). Making the shadow camera's area larger without increasing map size spreads the same pixel budget over more space, producing blocky, low-resolution shadows — the fix is either a smaller shadow camera box (tightly fit to the scene) or a larger map size (more memory, slower to compute). renderer.capabilities.maxTextureSize reports the hardware ceiling for map size.
Key Parameters
renderer.shadowMap.enabled— master switch; defaultfalse.light.castShadow— must be set per light for that light to produce a shadow map.mesh.castShadow/mesh.receiveShadow— set per mesh; a ground plane typically only needsreceiveShadow = true, while foreground objects typically need both.light.shadow.camera.{left,right,top,bottom,near,far,zoom}—DirectionalLight's shadow camera frustum; smaller area = sharper shadows for the same map resolution.light.shadow.mapSize.{width,height}— shadow map texture resolution, default512x512; larger costs more memory and compute.light.shadow.camera.{near,far}— forSpotLightandPointLight, objects closer thannearor farther thanfarnever receive a shadow from that light.renderer.capabilities.maxTextureSize— hardware ceiling on shadow map size.
When To Use
Enable real shadow maps when a scene's depth cues genuinely need cast shadows and the object/light count is small enough to afford the extra per-light render passes — see lighting for the renderer-level shadowMap.autoUpdate toggle that avoids recomputing shadows every frame once lighting is static. For scenes with many simple objects (characters, props) where a rough contact shadow suffices, a cheap fake shadow (a semi-transparent round texture on a plane, with depthWrite: false to avoid z-fighting between overlapping fake shadows) avoids the shadow-map cost entirely — the games Animal Crossing: Pocket Camp and Monument Valley are cited as using exactly this technique.
Risks & Pitfalls
- Forgetting any one of the three required flags (
renderer.shadowMap.enabled,light.castShadow,mesh.castShadow/receiveShadow) silently produces no shadow. - A
DirectionalLightshadow camera box that's too small clips shadows at its edges — useCameraHelperonlight.shadow.camerato see the actual coverage area. - Making the shadow camera box larger to "just cover everything" without raising
mapSizeproduces blocky, low-resolution shadows — area and resolution are independent knobs that must be tuned together. - Enabling shadows on many lights (or any
PointLight) multiplies per-frame render cost — each shadow-casting light means the whole scene is drawn again from its viewpoint, and aPointLightcosts 6x that on its own. - Placing a fake-shadow plane exactly at ground level (instead of a hair above it) can z-fight with the ground; the shadows source uses
y = 0.001anddepthWrite: falseto avoid this.
Related Concepts
Shadows are configured on the lights and renderer that also govern general illumination, and interact with the camera frustum concepts reused for each light's shadow camera. See also performance for the general principle of trading render passes for frame rate, and threejs overview.
Sources
raw/web_community-shadows.md— manual article: shadow-map mechanics and per-light/per-object render-pass cost, the fake-shadow-texture technique, enablingrenderer.shadowMap.enabled/light.castShadow/mesh.castShadow/receiveShadow,DirectionalLight'sOrthographicCamerashadow camera andCameraHelpervisualization,SpotLight'sPerspectiveCamerashadow camera tied toangle,PointLight's 6-direction shadow cost, and themapSizevs shadow-camera-area resolution tradeoff.
