Skip to content
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 shadow rendering performance by rendering static shadows and dynamic shadows separately for each light #4635

Open
Calinou opened this issue Jun 7, 2022 · 4 comments · May be fixed by godotengine/godot#77683

Comments

@Calinou
Copy link
Member

Calinou commented Jun 7, 2022

Related to #2745 and #4661 (technique from the same presentation).

Describe the project you are working on

The Godot editor 🙂

Describe the problem or limitation you are having in your project

Godot currently performs shadow map caching for all light types, but it will invalidate the entire texture (from both static and dynamic object casters) whenever it changes. This means the static geometry also needs to be redrawn on the shadow map whenever a dynamic object is moved.

This is especially taxing with DirectionalLight shadows, which update every frame as soon as an animated object is present in the scene (unless its Cast Shadow property is set to Off).

Describe the feature / enhancement and how it helps to overcome the problem or limitation

Improve shadow rendering performance by rendering static shadows and dynamic shadows separately for each light. This can be used to make caching more effective, especially in levels with complex static geometry.

Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams

Slides 44 to 50 of https://research.activision.com/publications/2021/10/shadows-of-cold-war--a-scalable-approach-to-shadowing mention a caching approach that could be used. Including screenshots of the most important 3 slides for reference, in case that link goes down:

2022-06-12_19 09 38

2022-06-12_19 10 52

2022-06-12_19 10 07

What would be done on the Godot side:

  • Add a Shadow Update Mode enum property to GeometryInstance3D with the following options:
  • Add a Shadow Update Mode enum property to Light3D with the following options:
    • Static (Fast)
    • Dynamic (Slow) (default)
    • If set to Static (Fast), the light will only cast shadows for objects marked as static. This can be used to improve performance on low-end configurations without compromising visuals too much, as an alternative to Add per-light shadow cull masks to control which objects cast shadows #3606.
      • To quickly scale down to low-end configurations (without requiring too much work from the user's side), it might be worth adding two project settings to enforce the Static update mode on all positional and directional lights.
    • Unlike GeometryInstance3D, I'd suggest the default value to be Dynamic (Slow) for Light3D. This won't have any performance impact if there are no objects marked as dynamic in the scene.
  • Render a static shadow map texture, which will only be re-rendered during gameplay if the light itself is changed in any way (e.g. its position is changed). Other static objects moving around the light will not cause the light to update its shadow map.
  • Render a dynamic shadow map texture, which is updated according to the current update policy.
  • When the dynamic shadow is updated, composite the dynamic shadow map texture on top of the static shadow map texture.
  • Render the final shadow map as usual.

If this enhancement will not be used often, can it be worked around with a few lines of script?

No.

Is there a reason why this should be core and not an add-on in the asset library?

The shadow rendering pipeline is core, and can't be overwritten by an add-on.

Keywords for easier searching: shadowmap, shadowmapping

@mrjustaguy
Copy link

Hmm.. For Directional Shadows this might not be very effective.

Any time the Camera Moves, the Directional Shadows would have to be re-rendered anyway even if all the geometry is static.

@Calinou
Copy link
Member Author

Calinou commented Jun 7, 2022

Any time the Camera Moves, the Directional Shadows would have to be re-rendered anyway even if all the geometry is static.

The shadow frustum is stabilized and snapped to prevent jittering when the camera moves, so I assume the shadow map only needs to be re-rendered when enough movement has occurred and repositioning is required. See also godotengine/godot#33340.

@BastiaanOlij
Copy link

Indeed for directional lights you often have that the larger cascades don't need to be updated regularly so this could speed it up as well. But the bigger benefit will be with normal lights

@Ansraer
Copy link

Ansraer commented May 2, 2023

I honestly don't think that this is the way to go. While having the concept of static geometry makes sense for shadows I can think of several other parts of the engine that could benefit from this as well.

If we are seriously considering this I think we should implement a general-purpose solution that makes it possible to mark nodes (and all their children) as static, making it impossible for the user to move them, change their mesh & collision shapes, or add any children. We would also need to explicitly block certain nodes & features (e.g. animation, custom vertex shaders, ...) in the static part of the scene hierarchy to make sure that nothing breaks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants