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:
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.
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.
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:
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:
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:
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.
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/
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:
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.
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.