Best shadow parameters for specific scene

Hi everybody,

I am trying to have the best shadow possible in a very specific scene. This scene includes only a model in the middle which fits in a box of 1x1x1. I followed every piece of advice from the documentation which is very well explained as usual :wink:

But I still can’t have the perfect shadow I am looking for ( or almost perfect haha )
Here is a playground with my parameters:

  • For my project, I need to use a directional light and a cascaded shadow.
  • I set stabilizeCascades to true because otherwise, it makes the shadow flickered.
  • I can’t have camera.maxZ < 50 because I have a skybox which needs to be visible like in the playground. I tried to play with light.shadowMaxZ/MinZ but it doesn’t change shadow quality like with camera.maxZ.

With those parameters, we can see some artefact on the watch glass and the shadow of the watch pointer is not that great.

Thanks for your help!


You don’t need the cascaded shadow generator for such a scene where you have a single small object to display.

When using a standard shadow generator, you can use the (new in 5.0) “Display Frustum” option of the inspector to tightly fit the frustum to the object:

Hello @Evgeni_Popov,

Thanks for your answer and test. I didn’t know there was a debug mode on classic shadows, this is very cool!

As we can see in your screenshot, there are some artifacts on the glass of the watch due to the shadow. And there is the same thing in my example here using cascaded shadow:

Is there a way to avoid that ?


Yes, it’s shadow acne. It occurs for objects that are both shadow caster and receiver. You can either remove receiveShadows = true for the watch mesh if not being shadow receiver is ok for you, or you need to tweak (raise) the bias value of the shadow generator. 0.01 seems to work well.

Yes the artifact is gone when setting the bias but then we don’t see the shadow being the watch pointer anymore so we lose in term of shadow quality:

I could maybe remove receiveShadows = true to the glass material as you suggest but we still see the artifact on other parts of the model even if it is less obvious.

What you need to do to really have good shadows in this example is to tighten as much as possible the view frustum and raise the shadow map size: CSM won’t help in this regard.

Also, using a full 32 bits float texture for the shadow map instead of 16 bits float can help a bit. By default, the shadow generator is using a 16 bits texture (if available): to force 32 bits, pass true for the 3rd parameter of the constructor.

Raising the shadow map size will also help you to have more precision in the bias value.

For eg (light frustum tighten as much as possible):

map=1024, bias=0.01 map=4096, bias=0.001

As there are more precision in PG2, I can lower the bias to make some shadows better / appear (see the minute hand / the second hand) without acne.


1 Like

Hi @Evgeni_Popov,

I just realized that directionnal light position is important in order to have a good shadow.
I guess it depends on model position and light direction?

As in my case the model will stay in the middle in a box of 1x1x1, I should be able to guess the right light position depending on its direction?

I appreciate your help :wink:

Yes, the position is used to define the light frustum: using the “Display frustum” debug option really helps to understand the impact of the changes in position / shadow min/max z on the frustum.

Indeed the “Display frustum” debug option really helps but is it available within BabylonJS library if I want to see it in my app? I haven’t saw it in the documentation.

Can you lead me on how to determine the best light position depending on its direction. I guess we have to put the light in the opposite way of its direction but I am not good at transformation between vector and rotation. :grimacing:

I think there might be an issue between blurKernel and shadowOnlyMaterial:
As you can see in this playground, when you change the blurKernel value, the shadow behind the watch pointer gets blurry but not on the ground.

Yes it is available in your code: BABYLON.DirectionalLightFrustumViewer

You need to play with the parameters and try to get what you want, but it’s not always possible. I have updated the shadow max Z / blurKernel / depthScale parameters and could get a fuzzy soft shadow on the ground but lost the shadows for the big hand in the process:

That would require some thinking… Will try to come up with something when I have a little time.

1 Like

very nice watch and real

1 Like

One way to do it is to get the bounding box of the mesh (+sub meshes), compute a bounding sphere for it and use the center of the sphere to compute a point on that sphere in the inverse direction of the light: that will be the light position. The shadow max Z is simply the diameter of the sphere:

Be careful, however, because it creates a quite tightly fit frustum of the mesh, but some filtering methods require that the frustum intersects with the mesh receiving the shadow (the plane in the PG): if you change the filtering method to PCF, you will see that the shadow is truncated because the frustum is not long enough: you need to raise the shadowMaxZ value.


Hi @Evgeni_Popov,

Thanks for the playground, it was really helpful to get to position the light in the best place possible.

I now have an issue with the shadow frustrum. As you can see in this playground, when playing with the blurKernel, you quickly reach the edge of the shadow:

I think this is what you warned me about in the precedent post?

But I didn’t find a way to get rid of it. I tried to play with shadowMaxZ or bias but it doesn’t change anything. And if I use FrustrumEdgeFallOff, I have other weird artefact showing up as you can see here:
I have a good result in this example but we can still see the edge a bit:

I can maybe activate FrustrumEdgeFallOff only when the blur get to big but is there a better solution?

Thanks for your help, Valentin

Yes, as there’s only the sphere as the shadow caster, you have a tight frustum:

You need to make a bigger frustum by disabling the automatic computation of the left/right/top/bottom bounds by setting light.autoUpdateExtends = false and by providing your own light.orthoLeft / light.orthoRight / light.orthoTop / light.orthoBottom values: