5

As per the screenshot, shadows cast onto the THREE.PlaneGeometry(250, 380, 1, 1) below are cut off.

scene

Steps I've taken to enable shadows

renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;

..

camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 1, 1000);

..

mainLight = new THREE.DirectionalLight(0xffffff, 0.5);
mainLight.position.set(50, 50, 50);
mainLight.castShadow = true;
mainLight.shadow.mapSize.width = width * window.devicePixelRatio;
mainLight.shadow.mapSize.height = width * window.devicePixelRatio;
mainLight.shadow.camera.near = 1;
mainLight.shadow.camera.far = 1000;
mainLight.shadow.camera.fov = 100;
scene.add(mainLight);

..

plane.receiveShadow = true;

..

model.castShadow = true;
model.receiveShadow = true;

I've played with different values like the shadow camera FOV and far plane values...

Is this a caveat with using DirectionalLight? I need even lighting across all of my models, as opposed to SpotLight.

I found three.js shadow cutoff but it simply suggested using a SpotLight instead and gave no explanation as to why that changes anything.

When I do use a SpotLight, I suddenly lose shadows on ground plane altogether.

-- Thanks

Rabbid76
  • 202,892
  • 27
  • 131
  • 174
Tom Chapman
  • 433
  • 7
  • 15

2 Answers2

9

See the three.js documentation for DirectionalLightShadow:

This is used internally by DirectionalLights for calculating shadows.

Unlike the other shadow classes, this uses an OrthographicCamera to calculate the shadows, rather than a PerspectiveCamera. This is because light rays from a DirectionalLights are parallel.

See further DirectionalLight

A common point of confusion for directional lights is that setting the rotation has no effect. This is because three.js's DirectionalLight is the equivalent to what is often called a 'Target Direct Light' in other applications.

This means that its direction is calculated as pointing from the light's position to the target's position (as opposed to a 'Free Direct Light' that just has a rotation component).

The reason for this is to allow the light to cast shadows - the shadow camera needs a position to calculate shadows from.


This means that the area affected by the shadow is defined by the position and the camera of the light source (DirectionalLight).

Set up the camera for the mainLight and define its orthographic projection for your needs:

mainLight.shadow.camera = new THREE.OrthographicCamera( -100, 100, 100, -100, 0.5, 1000 ); 
Rabbid76
  • 202,892
  • 27
  • 131
  • 174
  • You can set the directional light shadow camera top, right, bottom, and left cutoff distances. This forms a square shaped shadow. Is it possible to rotate the directional light / shadow so that the square fits in the camera as a square? – George May 29 '19 at 22:31
  • @George No there is nowhere a square. this is all 3D. The view volume for the camera and shadow camera is a frustum, defined by the perspective projection. Of course you can find the common volume (intersection volume) of the camera frustum and the light cone and define the minimal necessary shadow camera projection (frustum), which enclose this space. – Rabbid76 May 30 '19 at 06:52
  • 1
    How will I go about finding this minimal shadow camera projection? I have implemented my own crude solution which is good enough, basically changing the shadow camera top,right,bottom,left based on the camera angle, and the shadow and shadow target positions relative to the camera, but it would be better to have a theoretical optimal solution. – George May 30 '19 at 21:55
0

Note that performance versus quality is the big issue. Take this as a base to achieve optimum results,

const directionalLight1 = new THREE.DirectionalLight(0xffffff , 5); // Adjust intensity to a NICE VALUE through trial and error.
directionalLight1.position.set(0, 0, 20);
directionalLight1.castShadow = true;
// Shadow map width and height must be set to a value as low as possible without losing too much crispness. Experiment and adjust.
directionalLight1.shadow.mapSize.width = 1200; // Better readjust after changing directionalLight1.shadow.camera » top bottom left right
directionalLight1.shadow.mapSize.height = 1200; // Better readjust after changing directionalLight1.shadow.camera » top bottom left right
directionalLight1.shadow.camera.near = 0.1; // Near shadow casting distance
directionalLight1.shadow.camera.far = 100; // Far shadow casting distance
// Default shadow casting area is too small (T:5,L:-5,R:5,B:-5)
// Make it bigger but remember that we have to keep it at minimum to prevent the loss of visual prettiness!
directionalLight1.shadow.camera.top = 11;
directionalLight1.shadow.camera.bottom = -11;
directionalLight1.shadow.camera.left = -11;
directionalLight1.shadow.camera.right = 11;
scene.add( directionalLight1 );

HolyResistance
  • 594
  • 1
  • 8
  • 26