Directional light not showing shadows

Hello,

I’ve been trying to add shadows to my huge terrain. I’ve been thinking it would be best to add the shadow to the camera so while to camera moves around the shadows are only rendered for whats within the view of the camera.

Here is my attempt:

let root = new THREE.Group();
root.add(camera);

root.position.set(-3500, 50, -7800); // edge of terrain at -20000, 0, -20000
scene.add(root);

let light = new THREE.DirectionalLight(0xfffffff, 0.5);
light.position.set(0, 1, 0);
light.castShadow = true;
light.shadow.mapSize.width = 512;  
light.shadow.mapSize.height = 512; 
light.shadow.camera.near = 0.5;
light.shadow.camera.far = 500     

scene.add(new THREE.CameraHelper(light.shadow.camera));

scene.add(light);
root.add(light.shadow.camera);

I also did not forget to enable receiveShadows on the terrain and castShadows on the objects on the terrain.

But I only get this:

image

The shadow camera seems too small. But, if I make the map size beyond 4096, it just slows everything down.

What are my options here? I wanted to have soft shadows from a little angle from the top as if its around afternoon.

It seems you have not yet configured the frustum of the shadow camera correctly. Try it like mentioned here:

1 Like

I tried adding this:

const d = 100;

light.shadow.camera.left = - d;
light.shadow.camera.right = d;
light.shadow.camera.top = d;
light.shadow.camera.bottom = - d;

And got this:

image

Looks bigger but unfortunately still no shadows.

Are you sure that materials of your model are not THREE.MeshBasicMaterial()?

Yes, all are THREE.MeshLambertMaterial. Just to be thorough, I also added

 mesh.receiveShadow = true;
 mesh.castShadow = true

to everything in the scene. And made the frustum const d = 1000; to be huge but still nothing:

image

Is your city one big object? Or does it consist out of many single meshes?

Is it possible for you to demonstrate the issue with an editable fiddle?

BTW: Don’t forget to globally enable shadow maps like so:

renderer.shadowMap.enabled = true;
2 Likes

Yep, I also did that. Including setting the type of shadows to soft. The terrain consist of 1 big mesh, with about 225k vertices and about 500k+ faces. I know I should optimize this in someway but that would be another question as I have no idea. But for now, I’m having issues with shadows.

Unfortunately I can’t host it on websites like JSFiddle as I cannot include the object data. If you don’t mind, I uploaded it to my website. But I did cut it down to the simplest script with the problem.

You can find it here: https://blazekhan.com/public/map_viewer/
It could take awhile because the assets are bit large.

Thank you for taking your time.

Please try to switch to MeshPhongMaterial to see if it helps. Shadow quality with MeshLambertMaterial is poor in any case. It’s not a good choice for rendering shadows.

Besides, there seems to be something wrong with the normal data of your geometries. When I set the intensity of the ambient light to zero so only the directional light illuminates the scene, it looks like so:

image

I guess that’s not the intended visual result.

Should I just let THREE.JS calculate the normals? Instead of adding them from my assets?

I tried changing all materials from lambert to phong. Removed the ambient light. Tried fixing the normals to no success.

And I got this:

image

I’m starting to see shadows but I guess the normals issue is causing problems. The data is from DirectX so I tried multiplying the first part of the normals by -1 to make it work for OpenGL/WebGL. Maybe I’m doing ti wrong.


So I decided to just discard all normal data and let THREE.JS calculate all of it. Added the ambient light back to the scene and got this:

image

Still not very sure about the shadows though. Might be the positioning of the directional light?

With such a large frustum, shadow quality will be bad in any event. Try to keep the frustum as small as possible and also alter the angle of the light a bit (so it does not cast shadow from top).

I have the same issue… Trying to cast shadows on a loaded FBX with material MeshStandardMaterial. Works totally fine for point light. Nothing with directional light. :frowning:

plight shadow works fine:

this.plight = new THREE.PointLight( 0xffe673, this.env.plight_intensity,60 );

    this.plight.position.set(0,50,0 );

    this.plight.castShadow = this.env.shadows_on;

    this.plight.shadow.mapSize.width = 1024;

    this.plight.shadow.mapSize.height = 1024;

    this.plight.shadow.radius = 2;

    this.scene.add(  this.plight );

dlight shadow will not appear:

this.dlight = new THREE.DirectionalLight( 0xffffff, 0.33 );

    this.dlight.position.set(0.1,50,-0.1); 

    this.dlight.castShadow = this.env.shadows_on;

    this.dlight.shadow.mapSize.width = 1024;

    this.dlight.shadow.mapSize.height = 1024;

    this.dlight.shadow.radius = 2;

    this.dlight.shadow.camera.near = 0.5;    

    this.dlight.shadow.camera.far = 1000;     

    this.scene.add( this.dlight );

I have this problem right now. I’ll post back with what worked for me.

I have the same problem. Seems like the light position is exactly above the mesh so it might work if I alter the angle. But How do I do that?

Did Anyone solve this issue?

If someone can reproduce the issue with a simplified live example, I’m happy to provide feedback. Use this fiddle as a start template: https://jsfiddle.net/5ha20oLe/2/

Will try to re-construct the issue. Meanwhile - here is my issue on stack overflow with some code:

Seems like the light position is on the base plane

I’ve seen your post at stackoverflow earlier this day. Without a live example, it’s hard to investigate the issue.

BTW: You have this line in your code:

amblight.position.set(8, 10, 5); //default; light shining from top

Setting a position for an ambient light has no effect. It globally illuminates all objects in the scene equally.

Ofc!

I figured that out before, so that line is now gone. I also added my mesh as light.target which made the mesh look better. But is seems like the light is coming from another direction than the frustum. Do you have any idea where to start debugging this? I thought the frustum represented the bouding box for the light?

What you see visualized by the camera helper is the shadow camera. This camera is used to compute the shadow map of the light and does no represent the area which is illuminated by it.

Thank you! That explains a lot. I have two follow up questions in that case:

  1. how can I move the camera helper upwards aling the z-vactor? Would like to add a plane so that the buildings cast shadows on the ground. It still seams like the shadow camera is located at a point at the plane geometry that I want to cast shadows on.
  2. Is there a similar way to display where the light comes from? Would later on like to connect the sun poistion to longityude and latitude in mapbox

Two things that have been easy to overlook, leading to no shadows:

  • not setting renderer.shadowMap.enabled = true, so no shadows even if lights and objects have castShadow = true.
  • objects being too small in a far-away light’s shadow camera frustum, so make the light.shadow.camera.* parameters make the frustum encompass only the area you want shadow in (f.e. if you have a sun, set the near, far, etc, of the camera so that the camera box wraps tightly around the lit objects you’re looking at.