-
-
Notifications
You must be signed in to change notification settings - Fork 21k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improve DirectionalLight shadow rendering efficiency #57549
Comments
The first pic is in editor. I can't find it right now, but I recall discussing it, and it turns out the editor also counts the gizmos and the grid, hence more draw calls than you expect. Directional light basically renders the whole scene again when casting shadows, so careful tuning of directional light shadow distance and what meshes cast shadows is needed. You also have the option of using a simplified mesh for shadow casting, i.e. placing a second invisible mesh set to shadows only. |
Yes, but the gizmos and grid are not included in metrics while running the game. Shadow map rendering is expected to be a little more expensive, but not 400% more expensive. Even if the camera encompasses the whole scene, I would expect the worst case for the shadow rendering pass to be 2x everything. Do that with the repro scene and you'll see it tops out around 127 draw calls with DirectionalLight shadows enabled. Disable those shadows and fake them with spot lights and you only have 49 draw calls. |
There are also splits for a directional light, and depth prepass. The culling of the shadow volume against the view frustum is not that good either, I did a PR for this many moons ago .. #33340 . Ah yes I'd like to resurrect that at some point. Change your directional light to orthogonal and the verts rendered will drop a lot, it will be rendering shadow map once for every of the 4 splits you have it set to. |
Shadows in Godot generally need some optimizing, albeit things are much worse in 3.x compared to 4.0 On the topic of Culling godotengine/godot-proposals#3890 proposes a method to Improve on Shadow rendering, with the occlusion culling part also being applicable to Directional Shadows if that is doable however I'm unsure if that would improve performance as they have to be rerendered every frame and the main benefit of it is to do a quick check to see if you need to render a new shadow or not.. But that is by using GPU's Shadow Maps, Using Occlusion culling available in 4.0 for Normal Rendering just used to help with Directional Shadows on the other hand might benefit performance. Basically before rendering a given shadow in the shadowmap checks if there were any changes in the area that it's rendering (as in The radius around the Omni light, or Cone of the Spot Light, or less ideally their AABBs) and if there were changes there it pulls up the Last frame's Shadow to use as a Depth Buffer for Occlusion Culling, along with the Last frame's recorded object positions (to determine which objects were the casters in the last frame) and see if the Casters moved, or new Casters arrived in a possibly non shadowed area, in both cases requiring a new shadow rendering. |
Even though draw calls and primitive counts don't mean much on their own, this issue gets much worse in a real scene where several nodes have multiple materials and shaders. Even if they are hundreds of units behind the camera, we still end up paying for shader switches for these nodes that should be culled early on. Although if it could avoid the shader switching for shadow passes, that might be nice. And of course, some other tricks like rendering shadows with lower LOD meshes. |
See also godotengine/godot-proposals#3908, which would help improving the effective directional shadow map resolution (at the cost of slightly less effective object culling within each split). If we can combine both approaches together, we should be able to get back to the original efficiency level but with much greater effective shadow resolution.
This is feasible to implement in Godot 4.0 with the automatic mesh LOD, see godotengine/godot-proposals#2600. Godot 4.0 can also use dedicated shadow meshes that are automatically created on import. These shadow meshes weld vertices together to reduce memory bandwidth and vertex processing cost. In Godot 3.x, you can use shadow meshes manually by setting a MeshInstance's Cast Shadow property to Off, duplicating the mesh, clearing its material so it uses the fallback opaque material and setting the duplicate's Cast Shadow property to Shadows Only. You can then load a lower-quality version of the mesh. See also the Level of Detail add-on.
I'm not sure if this is always feasible, as shaders can influence where shadows are casted. Fully opaque materials are not affected by this, but this applies to any material that uses alpha scissor or opaque prepass. (Alpha blended materials can't cast shadows in Godot.) It's definitely something to look into, but it may already be done in the PS: If you need help navigating the rendering code, feel free to ask questions in the Godot Contributors Chat's |
I made a few minor changes to the repro project; Positioned the camera specifically so it can trigger a large change in the shadow map projection with just a minor change in camera angle. Use the space bar to switch between the two angles (the difference is only 1 degree). I also changed the shadow mode to 2 splits and the depth range to optimized which shows the issue pretty well. There are other camera angles and shadow settings that are far worse. And throwing various things into the scene can completely resolve the "disappearing shadow" issue, but I don't know why. Render Culling Test-optimized shadowmap.zip And I also captured the frames between the two minor camera angles in RenderDoc. Here are the interesting textures. Frame buffer at -50 degrees: Shadow map at -50 degrees: Frame buffer at -51 degrees: Shadow map at -51 degrees: Looks like the light-space projection is very inaccurate. I'll test the patch in #43207, I'm wondering how much it will help in this case. This is definitely related to the issue. Hiding SportsCar makes both camera angles look like the second image, and fewer things get drawn. |
Oof, sorry for the long delay! I finally got around to updating one of my projects to 3.5 (from 3.4.4!) so I'm still behind and getting caught up. I'll take a look at making sure my dev environment for Godot still works, so I can build the latest 3.x and try that patch. The repro scene with the cars definitely still has the issue shown above in 3.5, though. That is one of the things I tested while checking out 3.5... I don't have a strict timeline on doing any of this, but I kind of have the interest again. |
Fixed by #82584. |
Godot version
3.4.2
System information
Windows 11, GLES3, NVIDIA RTX 3090 (511.23)
Issue description
A screenshot was posted to the discord memes channel a few days ago, and I got curious. The screenshot is below, along with the original comment.
I created a test scene with a few objects and found that DirectionalLight with shadows enabled was the biggest issue. There is also some odd behavior with this combination that looks like false positives in the renderer culling, but I haven't found any reason for it while looking through the spatial partitioning code. At certain camera angles, objects that are no where near the camera frustum will contribute to the "Vertices Drawn", "Surface Changes", and "Draw Calls" metrics in the monitor.
Here are some screenshots from the test procedure described below. First, the scene is running, and I have the monitor shown in the editor:
Then I hide the SportsCar node, and "Vertices Drawn" is reduced by over 20,000! Surface Changes and Draw Calls also see some improvements:
Then I turn SportsCar back on and disable the shadow on DirectionalLight. Vertices Drawn has reduced by almost 60,000 (a massive 76% improvement)! and Surface Changes and Draw Calls see a 75% reduction; another huge improvement!
And finally, changing SportsCar visibility with the shadow off has no impact whatsoever, exactly what is expected for a node that cannot be seen by the camera at all.
This might be the cause of #35746.
Steps to reproduce
Minimal reproduction project
Render Culling Test.zip
The text was updated successfully, but these errors were encountered: