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

Mixed cached shadow maps (Cached static + dynamic rendered every frame) #1559

Merged

Conversation

FrancescoC-unity
Copy link
Contributor

@FrancescoC-unity FrancescoC-unity commented Aug 10, 2020

IMPORTANT: Requires this C++ PR https://ono.unity3d.com/unity/unity/pull-request/110280/_/graphics/concept-of-static-shadow-caster-for-SRP . Whenever that lands I will bump minimum required version of HDRP.

This PR introduces two features.

The biggest feature is

Mixed cached shadow maps

It is now possible to tag objects as Static Shadow Casters (with the C++ PR above). Selecting the new option Always draw dynamic on the shadows, it is possible to have all static objects cached and dynamic objects rendered on top every frame.

See the example in the gif, all pillars are marked as static (when moved the shadow is not updated) and the dancing dude is not. You can see how its shadow is updated every frame while the rest of the shadows are not.

MixedCached

This will allow the cached shadows to be used in many more use cases. Lots of games have static environments but few movable characters and objects, so before those couldn't use cached shadows, but now can for significant perf benefits (both CPU and GPU as most of the objects will not be rendered anymore in shadows).

Shortly, this work rendering first all static in cached atlas when an update is required, then blit from cache to dynamic atlas and subsequently every frame the dynamic objects are rendered on top of the blitted cached shadows.

Second feature is:

Update upon light movement

There is now a new option to shortcut a usual use case for OnDemand. This will now trigger an update every time a light moves above a certain threshold set via script (by default really small).

This helps for use cases when a light almost never moves, but you want to update when it does move.

UpdateLightMovement

Note: Rendergraph has not been updated, need a bit of a chat with @JulienIgnace-Unity to make sure of a change I need to do.

What have I tested: I tested what you can see in the gif, all the light types for both new options. I also tried when the dynamic atlas is full and the mixed cached shadows still work.
Still I'd love thorough testing on this since I changed a few fundamental things about what determines shadow rendering options.

…ters

# Conflicts:
#	com.unity.render-pipelines.high-definition/Runtime/Lighting/Light/HDAdditionalLightData.cs
@@ -108,6 +108,10 @@ When a Light that caches its shadows renders its shadow map for the first time,
If the Light's **Update Mode** is set to **OnDemand**, you can manually request HDRP to update the Light's shadow map. To do this, access the Light's **HDAdditionalLightData** component and call the `RequestShadowMapRendering` function. Also, if the Light has multiple shadows (e.g. multiple cascades of a directional light), you can request the update of a specific sub-shadow. To do this, use the `RequestSubShadowMapRendering(shadowIndex)` function.
For a Light that does cache its shadows, if you disable it or set its **Update Mode** to **Every Frame**, you can tell HDRP to preserve the Light's shadow map's place in the cached shadow atlas. This means that, if you enable the Light again, HDRP does not need to re-render the shadow map or place it into a shadow atlas. For information on how to make a Light preserve its shadow map's place in the cached shadow atlas, see [Preserving shadow atlas placement](#preserving-shadow-atlas-placement).

As a shortcut for a common case, HDRP offers an option to automatically trigger an update when either the position or rotation of a light changes above a certain threshold. This option can be selected in the Shadow settings section on the Light as **Update on light movement**.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a shortcut for a common case, HDRP offers an option to automatically trigger an update when either the position or rotation of a light changes above a certain threshold. To enable this option, select a Light and, in the Shadow section of its Inspector, enable Update on light movement.

@@ -108,6 +108,10 @@ When a Light that caches its shadows renders its shadow map for the first time,
If the Light's **Update Mode** is set to **OnDemand**, you can manually request HDRP to update the Light's shadow map. To do this, access the Light's **HDAdditionalLightData** component and call the `RequestShadowMapRendering` function. Also, if the Light has multiple shadows (e.g. multiple cascades of a directional light), you can request the update of a specific sub-shadow. To do this, use the `RequestSubShadowMapRendering(shadowIndex)` function.
For a Light that does cache its shadows, if you disable it or set its **Update Mode** to **Every Frame**, you can tell HDRP to preserve the Light's shadow map's place in the cached shadow atlas. This means that, if you enable the Light again, HDRP does not need to re-render the shadow map or place it into a shadow atlas. For information on how to make a Light preserve its shadow map's place in the cached shadow atlas, see [Preserving shadow atlas placement](#preserving-shadow-atlas-placement).

As a shortcut for a common case, HDRP offers an option to automatically trigger an update when either the position or rotation of a light changes above a certain threshold. This option can be selected in the Shadow settings section on the Light as **Update on light movement**.
The threshold that are used to determine how much a light needs to move or rotate to trigger an update can be customized per light with the properties
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can customize the threshold that HDRP uses to determine how much a light needs to move or rotate to trigger an update. To do this, use the:

@@ -108,6 +108,10 @@ When a Light that caches its shadows renders its shadow map for the first time,
If the Light's **Update Mode** is set to **OnDemand**, you can manually request HDRP to update the Light's shadow map. To do this, access the Light's **HDAdditionalLightData** component and call the `RequestShadowMapRendering` function. Also, if the Light has multiple shadows (e.g. multiple cascades of a directional light), you can request the update of a specific sub-shadow. To do this, use the `RequestSubShadowMapRendering(shadowIndex)` function.
For a Light that does cache its shadows, if you disable it or set its **Update Mode** to **Every Frame**, you can tell HDRP to preserve the Light's shadow map's place in the cached shadow atlas. This means that, if you enable the Light again, HDRP does not need to re-render the shadow map or place it into a shadow atlas. For information on how to make a Light preserve its shadow map's place in the cached shadow atlas, see [Preserving shadow atlas placement](#preserving-shadow-atlas-placement).

As a shortcut for a common case, HDRP offers an option to automatically trigger an update when either the position or rotation of a light changes above a certain threshold. This option can be selected in the Shadow settings section on the Light as **Update on light movement**.
The threshold that are used to determine how much a light needs to move or rotate to trigger an update can be customized per light with the properties
`cachedShadowTranslationUpdateThreshold` and `cachedShadowAngleUpdateThreshold` on the **HDAdditionalLightData** component. Note that point lights ignore the angle differences when determining if an update is needed with this mode.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cachedShadowTranslationUpdateThreshold and cachedShadowAngleUpdateThreshold properties on the Light's HDAdditionalLightData component. Note that point lights ignore the angle differences when determining if they need to perform an update in this mode.

@@ -131,6 +135,16 @@ Note that this causes HDRP to mark all the shadow maps in the atlas as dirty whi
If you disable the Light or change its **Update Mode** to **Every Frame**, the cached shadow manager unreserves the Light's shadow map's space in the cached shadow atlas and HDRP begins to render the Light's shadow map to the normal shadow atlases every frame. If the cached shadow manager needs to allocate space on the atlas for another Light, it can overwrite the space currently taken up by the original Light's shadow map.
If you plan to only temporarily set a Light's **Update Mode** to **Every Frame** and want to set it back to **On Enable** or **On Demand** later, you can preserve the Light's shadow map placement in its atlas. This is useful, for example, if you want HDRP to cache a far away Light's shadow map, but update it every frame when it gets close to the [Camera](HDRP-Camera.md). To do this, access the Light's **HDAdditionalLightData** component and enable the **preserveCachedShadow** property. If this property is set to `true`, HDRP preserves the Light's shadow map's space in its shadow atlas. Note that even if this property is enabled, if you destroy the Light, it loses its placement in the shadow atlas.

### Mixed Cached Shadow Maps

It is possible to cache only a portion of the shadow map. In order to do so, you must check the **Always draw dynamic** option in the shadow settings of the light and then tick the **Static Shadow Caster** checkbox all renderers that will be cached in shadows (in the Renderer component).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is possible to cache only a portion of the shadow map. To do this, enable the Always draw dynamic option in the Light's shadow settings and then enable the Static Shadow Caster option for all Renderers to cache shadows for.

### Mixed Cached Shadow Maps

It is possible to cache only a portion of the shadow map. In order to do so, you must check the **Always draw dynamic** option in the shadow settings of the light and then tick the **Static Shadow Caster** checkbox all renderers that will be cached in shadows (in the Renderer component).
With such setup, all static shadow casters are rendered in the shadow map only whenever an explicit update is requested (or only on light enable in case of **OnEnable** update mode), while the other casters (dynamic shadow casters) are drawn each frame.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With this setup, HDRP only renders static shadow casters into the shadow map when an explicit update is requested (or, if the Light uses the OnEnable update mode, only when the Light enables) whereas, for dynamic shadow casters, HDRP renders them into their respective shadow maps each frame.


It is possible to cache only a portion of the shadow map. In order to do so, you must check the **Always draw dynamic** option in the shadow settings of the light and then tick the **Static Shadow Caster** checkbox all renderers that will be cached in shadows (in the Renderer component).
With such setup, all static shadow casters are rendered in the shadow map only whenever an explicit update is requested (or only on light enable in case of **OnEnable** update mode), while the other casters (dynamic shadow casters) are drawn each frame.
This setup is particularly useful if for example your environment is mostly static and the light doesn't move, but there are few dynamic objects that will need shadows to be cast by such lights. In such scenarios, setting the light as having a mixed cached shadow map will greatly improve performance both on CPU and GPU.
Copy link
Contributor

@JordanL8 JordanL8 Aug 10, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This setup is particularly useful if your environment consists of mostly static GameObjects and the lights do not move, but there are few dynamic GameObjects that you want the static lights to cast shadows for. In such scenarios, setting the light to have a mixed cached shadow map greatly improves performance both on the CPU and GPU.

With such setup, all static shadow casters are rendered in the shadow map only whenever an explicit update is requested (or only on light enable in case of **OnEnable** update mode), while the other casters (dynamic shadow casters) are drawn each frame.
This setup is particularly useful if for example your environment is mostly static and the light doesn't move, but there are few dynamic objects that will need shadows to be cast by such lights. In such scenarios, setting the light as having a mixed cached shadow map will greatly improve performance both on CPU and GPU.

Please note that as part of implementation details, if a shadow is set up as mixed cached, every frame a blit from the cached shadow map to the dynamic atlas is performed. This is important to keep in mind both for the extra runtime cost of the blit in itself, but also to understand that in terms of memory a single shadow map will require space in both atlases.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that, due to implementation details, if you set up a shadow to be mixed cached, HDRP performs a blit from the cached shadow map to the dynamic atlas. This is important to keep in mind both for the extra runtime cost of the blit in itself, but also to understand that, in terms of memory, a single shadow map requires space in both atlases.


Please note that as part of implementation details, if a shadow is set up as mixed cached, every frame a blit from the cached shadow map to the dynamic atlas is performed. This is important to keep in mind both for the extra runtime cost of the blit in itself, but also to understand that in terms of memory a single shadow map will require space in both atlases.

Another important note, is due to implementation details, if a light with mixed cached shadows moves and the cached counterpart is not updated, the result will look wrong. In such cases either select the **Update on light movement** or the update mode to **OnDemand** and make sure an update is triggered when desired.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another important note, also due to implementation details, if a Light with mixed cached shadows moves and you do not update the cached counterpart, the result looks wrong. In such cases either enable the Light's Update on light movement option or set the Light's update mode to OnDemand and make sure to trigger an update when you move the Light.

Copy link
Contributor

@JordanL8 JordanL8 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added some suggestions to the docs

@FrancescoC-unity
Copy link
Contributor Author

Fixed up docs as suggested @JordanL8

@iM0ve
Copy link
Contributor

iM0ve commented Aug 12, 2020

Review in Progress

ISSUES

  1. Tooltip has a space before the text

image

  1. [Addressed in a comment below]. Setup is a bit confusing. Do we have a restriction that's why it's like that?

From the documentation, it notes that this feature is useful because most of the time MAJORITY of the assets are static but you have a few that you want dynamic shadows for. But the implementation for the user is completely opposite, instead of changing a few objects to get dynamic shadows, when you check Always draw dynamic - all of your shadows become dynamic, forcing you to go over every other object and fix them to get the static shadows back.

Can the Always draw dynamic checbox be removed or we need it?
If by default Always draw dynamic is always on(remove parameter) and the Static shadow caster is also Enabled by default on all game objects (dont remove parameter). Then usage would be very simplified, all you have to do is uncheck Static shadow caster for objects you want realtime shadows for and thats it. Again Im just looking at this from usage convenience, maybe there is a technical reason we can not implement it like this.

  1. [Addressed in a comment below]. This one is subjective but the name of the checbox sounds like it has to do something with Static property when it doesnt. Alternative naming suggestions Shadow Map Static or Realtime Cached Shadows. Goal is to have a name that would associate the feature with cached shadow maps and not static property/baked shadows.

image

  1. [Addressed in a comment below]. When changing the shadow cast mode on the object, the shadow gets duplicated. To fix this you have to enable/disable the light to refresh atlas. I understand that likely we cant improve this behaviour but still wanted to ask if maybe its possible to see immediate update when changing gameobject property without toggling the light.

M0g8Q5mpqc

  1. [Fixed] Somethings wrong with the Directional light shadow. In the video expected result would be for the cube to have a dynamic shadow. But instead, you can "paint" shadows on the floor. Toggling directional light does not fix it. With spot, area and point feature works fine.
    capture

Test coverage

  • Tooltips
  • Documentation
  • Typical usage/Real-world usage
  • Performance
  • Tested all light types
  • Build player (dx11 dx12 vulkan)
  • Mac (not tested)

Additional notes:

  • Performance
    Scenario [1]: 11 spotlights with OnEnable shadows 60 fps
    Scenario [2]: 11 spotlights with OnEnable + Always Draw dynamic + all objects dynamic 40 fps
    Scenario [3]: 11 spotlights with OnEnable + Always Draw dynamic + all objects static 50 fps
    Scenario [4]: 11 spotlights with OnEnable + Always Draw dynamic + some objects static. Between 40 - 50 fps

I assume as expected. By enabling the feature on all lamps you loose 10 fps from copying atlas (8k). Then the more game objects you turn to cached realtime casters, fps slightly drops accordingly.

  • Before this implementation you could not use tessellated materials. Now like with dynamic objects, you can make them cast shadow realtime (expensive but at least doesn't look broken).

  • Second feature(Update on light transform) is very simple but useful.

@FrancescoC-unity
Copy link
Contributor Author

1- Will fix, though it is on C++ side so it will be a bit of a mess :-) Will see for next release.

2- Several points to make here:

The checkbox we absolutely need otherwise all cached shadows will be mixed and it is not what we want. We need to have a discrimination of the two types. Sometimes you are fine with dynamic stuff being rendered for one light and not for another and to differentiate you need a checkbox on the light.

The checkbox on the renderer is engine side and it is going to be used by all SRPs, we cannot make it default (changing default behaviour) for HDRP usages. So while I agree, if it was hdrp only I would've made that on by default, we cannot really do that.

3- The static checkbox up there is lost at runtime, so it is unusable sadly. Again, the reason for the naming is due to it being a generic engine side thing and its implementation is generic to any Shadow map draw call that is done via SRP, so cannot really make it reference cached shadow maps. The tooltip is there to show a potential usage linking to what HDRP does.

@iM0ve
Copy link
Contributor

iM0ve commented Aug 13, 2020

Thanks, added 4 and 5.

@FrancescoC-unity
Copy link
Contributor Author

4- I agree that it is a bit bad, but unfortunately we can't fix this case as we don't have a list of game objects that are in each state on C# nor easily in C++. If we ever will have scriptable culling then this could change at some point; as of now sadly not.

5- I think i know what's the issue! I am in vacation, but will check and fix when back.

@FrancescoC-unity
Copy link
Contributor Author

I disabled the directional light case, to make it work we need even more memory. Waiting for the RG to be main case to do that.
It is the least useful case anyway (since directional light shadows need frequent re-updates).

Ping @iM0ve re the bug above on the directional light.

Copy link
Contributor

@iM0ve iM0ve left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The issue 5 still reproduces for me.
I have:

  • Pulled the disable/fix
  • Deleted the Library folder
  • Tried turning Directional light on/off

asdgewg

Copy link
Contributor

@iM0ve iM0ve left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All issues fixed or addressed

…ters

# Conflicts:
#	com.unity.render-pipelines.high-definition/Runtime/Lighting/Shadow/HDShadowAtlas.cs
#	com.unity.render-pipelines.high-definition/Runtime/RenderPipelineResources/HDRenderPipelineResources.asset
@sebastienlagarde sebastienlagarde merged commit 26d008e into HDRP/staging Oct 7, 2020
@sebastienlagarde sebastienlagarde deleted the HDRP/cached-shadows-with-dynamic-casters branch October 7, 2020 22:34
sebastienlagarde added a commit that referenced this pull request Oct 8, 2020
* Fix warning in ShaderPassDecal.hlsl

* Tooltip for microshadows (#2100)

* Update Alembic version to stop Alembic complaining

* Disable quad overdraw on ps4 (#2107)

* disable quad overdraw on ps4

* update doc

Co-authored-by: sebastienlagarde <sebastien@unity3d.com>

* Enable the DXR Vertex Format test for RG (#2022)

* Add option to enable pure specular planar reflection (optimization) (#2105)

* - Added a rough refraction option on planar reflections.
- Added a rough distortion frame setting and and info box on distortion materials.
- Added tests to cover the new features.

* review changes

* removed PerceptualRoughnessToMipmapLevel_LODFlag which isn't required.

* clean more code (remove OutputSmoothPlanarReflection)

* missing one update

* removed rough distortion

* remove distortion test scene

* Update DistortionUIBlock.cs

* clean distortion

* update documentation

* address doc feedback

* Update Upgrading-from-2020.1-to-2020.2.md

* Update Upgrading-from-2020.1-to-2020.2.md

* update test 2220

* update smoothness test

* Create 2220_SmoothPlanarReflection.png

* update reference screenshots

Co-authored-by: Anis <anis@unity3d.com>

* Implementing scalabity settings for planar resolutions (#2059)

* - Added a rough refraction option on planar reflections.
- Added a rough distortion frame setting and and info box on distortion materials.
- Added tests to cover the new features.

* review changes

* Added scalability settings for the planar reflection resolution.

* support migration.

* renamed the resolution parameters for the planar reflections

* removed PerceptualRoughnessToMipmapLevel_LODFlag which isn't required.

* clean more code (remove OutputSmoothPlanarReflection)

* missing one update

* removed rough distortion

* remove distortion test scene

* Update DistortionUIBlock.cs

* clean distortion

* update documentation

* address doc feedback

* Update Upgrading-from-2020.1-to-2020.2.md

* Update Upgrading-from-2020.1-to-2020.2.md

* update test 2220

* Revert "renamed the resolution parameters for the planar reflections"

This reverts commit 753af88.

* cleanup migration code

* update smoothness test

* Create 2220_SmoothPlanarReflection.png

* some cleanup

* Revert "Revert "renamed the resolution parameters for the planar reflections""

This reverts commit fbf3572.

* update doc

Co-authored-by: sebastienlagarde <sebastien@unity3d.com>

* Remove useless SampleSkyTexture function

* Fix errors when resizing the compositor's output and when removing/adding the compositor (#2118)

# Conflicts:
#	com.unity.render-pipelines.high-definition/CHANGELOG.md

* Remove BLEND keywords and replace with constant branch  (#1884)

* Removing blend keyword.

* Changelog

* Update DecalProperties.hlsl

* revert change in AxF shader

* fix compile issue with ordering when using VT

* update comment

* Update Upgrading-from-2020.1-to-2020.2.md

* update comment + optimized code version (in case of bad compiler)

* update comment revert (wrong) optimization

* remove unused multicopmile in test

* candidate fix for VFX

* Revert "candidate fix for VFX"

This reverts commit e3183f0.

* Another tentative fix

* Try differently

* Fix compile issue.

* Another vfx fix attempt.

* Latest flavour of fix.

* Remove old defines

* Fix hybrid

* Update includes in ray tracing

* Update Upgrading-from-2020.1-to-2020.2.md

Co-authored-by: Sebastien Lagarde <sebastien@unity3d.com>

* Mixed cached shadow maps (Cached static + dynamic rendered every frame) (#1559)

* UI for mixed cached shadows

* Baby steps (# 1)

* Baby steps (# 2)

* Extrapolate the update request data into a function

* Commit before blit code

* Need to switch branch

* Need to switch branch

* Working as long the light does not move.

* Fixed area light case

* Make it render graph ready

* Make sure we correctly update the freshly updated cached shadows

* Raw update upon movement option (Missing API for threshold)

* Threshold API

* Add unity binary tag

* New docs

* Shader name change

* Update docs

* Fix bad merge

* Update Shadows-in-HDRP.md

* For now disable the directional light case.

* Disable directional mixed.

* Make it disabled under define

* Remove docs

* Implement rendergraph version

* Remove superflous blit

* Tentative fix for yamato.

Co-authored-by: JordanL8 <lewis.jordan@hotmail.co.uk>

* update meta file with wrong information

Co-authored-by: Adrien de Tocqueville <adrien.tocqueville@unity3d.com>
Co-authored-by: anisunity <42026998+anisunity@users.noreply.github.com>
Co-authored-by: Anis <anis@unity3d.com>
Co-authored-by: Pavlos Mavridis <pavlos.mavridis@unity3d.com>
Co-authored-by: FrancescoC-unity <43168857+FrancescoC-unity@users.noreply.github.com>
Co-authored-by: JordanL8 <lewis.jordan@hotmail.co.uk>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
5 participants