ShadowMesh
Three.js’s ShadowMesh addon is a very performant alternative to shadow mapping. It makes use of the stencil buffer and a locally computed matrix to create very simple shadows.
controls
Limitations
Section titled “Limitations”Although the ShadowMesh addon is very perfomant, it does come with a few limitations
- Shadows can only be cast onto flat planes.
- It does not support soft shadows.
- Like most things in three.js, it is not reactive - if you update the reference mesh’s geometry, you must also update the shadow mesh’s geometry to match.
In order for the shadow mesh to work it needs a plane, a light source, and a shadow-casting mesh.
-
Enable the renderer’s stencil buffer.
const renderer = new WebGLRenderer({canvas,stencil: true,}) -
Create the ShadowMesh instance and add it to the scene.
const shadowMesh = new ShadowMesh(mesh);scene.add(shadowMesh); -
Create the plane that will be used when computing the shadow’s matrix.
const plane = new Plane(planeNormal, planeConstant); -
Create the light source.
const light = new DirectionalLight();light.position.set(/* */);const light4D = new Vector4(...light.position, 0.1); -
Update the shadow mesh as needed using the plane and light source.
shadowMesh.update(plane, light4D);
Offsetting and Orienting the Plane
Section titled “Offsetting and Orienting the Plane”In order to avoid z-fighting, the plane should be offset slightly from the floor or ground which the shadow will be casted on.
const offset = 0.01;plane.constant = offset;Unfortunately there’s not an easy way to get a Plane instance from a PlaneGeometry without accessing the geometry’s buffer attributes. Instead, you can create the plane and then rotate the mesh to look at the plane’s normal.
const floor = new Mesh(new PlaneGeometry());const yHat = new Vector3(0, 1, 0);const plane = new Plane(yHat, 0.01);
floorMesh.lookAt(plane.normal);Light Source
Section titled “Light Source”The light source should be a 4D Vector where the w component of the vector controls the spread of the shadow. A w value close to 0 is used for directional lights whereas a value closer to 1 is more suited for point lights.
The light source does not need to be added to the scene as the only information that is needed is the light’s position.
For example, if you have a directional light positioned somewhere in the scene that should cast a shadow, all you need is the light’s position.
const lightPosition = new Vector3(1, 1, 1).normalize().multiplyScalar(5);const lightPosition4D = new Vector4(...lightPosition, 0.1);
shadowMesh.update(plane, lightPosition4D);