'How to create directional light shadow in Three.JS?

Is it possible to create shadows from a DirectionalLight?

If I use SpotLight then I see a shadow, but if I use DirectionalLight it doesn't work.



Solution 1:[1]

Yes, you most definitely can use directional lights to cast shadows. You need to make sure you are not using MeshBasicMaterial as they don't support shadows. Use MeshLambertMaterial or MeshPhongMaterial instead.

You need to enable shadows for the renderer with something along these lines:

renderer.shadowMapEnabled = true;
renderer.shadowMapSoft = true;

renderer.shadowCameraNear = 3;
renderer.shadowCameraFar = camera.far;
renderer.shadowCameraFov = 50;

renderer.shadowMapBias = 0.0039;
renderer.shadowMapDarkness = 0.5;
renderer.shadowMapWidth = 1024;
renderer.shadowMapHeight = 1024;

And then you must enable shadow casting and shadow receiving per object and per light so you would have

dirLight.castShadow = true;
object.castShadow = true;
otherObject.receiveShadow = true;

Then, if the light and objects are placed at appropriate positions. dirLight will cause the shadow of object to be cast against otherObject.

[EDIT]: Here is a working demo for anyone looking to do something similar.

Solution 2:[2]

Be aware that shadow maps are scale dependent. I'm working on a scene where the unit distance represents one metre, and my objects are around 0.4 metres large. This is quite small by Three.js standards. If you have this situation too, then you can take a few important steps:

  • Ensure the shadow camera's near/far planes are reasonable given your scene's dimensions.
  • Ensure the shadow camera top/left/bottom/right values are not too large, otherwise each shadow 'pixel' may be so large that you don't even notice the shadow in your scene.

Let's look at how to do this.

Debugging

Be sure to turn on the debug rendering per light via CameraHelper:

scene.add(new THREE.CameraHelper(camera)) 

Or in older versions of the Three.js:

light.shadowCameraVisible = true;

This will show you the volume over which the shadow is being calculated. Here is an example of what that might look like:

Notice the near and far planes (with black crosses), and the top/left/bottom/right of the shadow camera (outer walls of the yellow box.) You want this box to be tight around whatever objects you are going to have in shadow — possibly even tighter than I'm showing here.

Code

Here are some snippets of code that might be useful.

var light = new THREE.DirectionalLight(0xffffff);
light.position.set(0, 2, 2);
light.target.position.set(0, 0, 0);
light.castShadow = true;
light.shadowDarkness = 0.5;
light.shadowCameraVisible = true; // only for debugging
// these six values define the boundaries of the yellow box seen above
light.shadowCameraNear = 2;
light.shadowCameraFar = 5;
light.shadowCameraLeft = -0.5;
light.shadowCameraRight = 0.5;
light.shadowCameraTop = 0.5;
light.shadowCameraBottom = -0.5;
scene.add(light);

Make sure some object(s) cast shadows:

object.castShadow = true;

Make sure some object(s) receive shadows:

object.receiveShadow = true;

Finally, configure some values on the WebGLRenderer:

renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(canvasWidth, canvasHeight);
renderer.shadowMapEnabled = true;
renderer.shadowMapSoft = true;

Solution 3:[3]

In r139.2 the attribues has been change to:

renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap; //THREE.BasicShadowMap | THREE.PCFShadowMap |  THREE.VSMShadowMap | THREE.PCFSoftShadowMap

renderer.shadowMap.soft = true;
renderer.shadowMap.bias = -0.0001;
renderer.shadowMap.darkness = 1;
renderer.shadowMap.width = 2048;
renderer.shadowMap.height = 2048;

sample:

const spotLight = new THREE.DirectionalLight();
spotLight.castShadow = true;
scene.add(spotLight);
spotLight.position.set(0, 8, 0);


//Object cast shadow
const cube = new THREE.Mesh( new THREE.BoxGeometry(1,1,1), new THREE.MeshBasicMaterial({color: 0x00ff00}) );
cube.castShadow = true;
scene.add(cube);


//Plane floor receive shadows
const planeShadow = new THREE.Mesh(new THREE.PlaneGeometry(3, 3), new THREE.MeshPhongMaterial()); //Material they have to be MeshPhongMaterial

planeShadow.name = "FloorShadow";
planeShadow.renderOrder = -2;
planeShadow.lookAt(new THREE.Vector3(0, 1, 0));
planeShadow.receiveShadow = true;
planeShadow.position.set(0, 0, 0);
scene.add(planeShadow);

If you need more info or other example, you can see this link [https://sbcode.net/threejs/directional-light-shadow/]

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2
Solution 3 211 - Anthony Sychev