Correctly handle visibility ranges in shadow maps.#24289
Conversation
Right now, visibility ranges are always resolved relative to the view. This is incorrect for shadow maps in two ways: 1. Visibility ranges for meshes in directional light shadow maps should be resolved relative to the camera that the cascades are associated with. 2. Visibility ranges for meshes in point and spot light shadow maps should be resolved relative to *some* camera. To properly solve (2), this commit introduces the notion of a *primary camera*. The primary camera is the one that visibility ranges are relative to, when rendering views not associated with any camera. Point and spot light shadow maps are currently not associated with a camera, and therefore we need this extra notion in order to properly evaluate visibility ranges. (As a follow-up, we should introduce the notion of *own shadow maps*, which will allow each camera to have separate shadow maps for point and spot lights. That feature is however out of scope for *this* patch, which simply seeks to make the existing semantics consistent.) A new component, `PrimaryCamera`, has been added, which allows the developer to customize which camera is primary. In the absence of this component, this PR implements a simple heuristic to determine the primary camera: to prefer cameras that render to a window. This heuristic should suffice in the vast majority of cases, so developers will rarely have to manually use the `PrimaryCamera` component. A new field, `lod_view_world_position`, has been added to `View` to supply the position of the primary camera to the GPU. This is much simpler than introducing a new uniform or using immediates, as bevyengine#24197 tried to do. This commit is the proper fix for bevyengine#23991. PR bevyengine#24252 attempted to fix this problem by reverting bevyengine#23115. However, this didn't actually fix the issue, because the semantics were still inconsistent. This commit constitutes the correct fix for the issue. I verified that, after un-reverting bevyengine#23115 on top of this patch and modifying it to use the new `lod_view_world_position`, that the issue reported in bevyengine#23991 disappears.
|
You may be wondering why reverting #23115 seemed to work. The answer is that, when CPU culling is in use, all meshes are submitted to the GPU if they're visible in any camera, as determined by the visibility ranges. This means that the visibility ranges mostly work for the shadows in the After this PR lands, I intend to submit a follow-up PR to unrevert #23115, updated to use the new |
|
Should CPU culling for point light shadow maps be changed to be relative to PrimaryCamera, too? |
|
It should effectively be already, because the "CPU culling" is really just culling shadow maps that aren't in view of any camera (see |
Maybe I'm wrong, but shouldn't we cull meshes that aren't in view of PrimaryCamera here instead of any camera? bevy/crates/bevy_light/src/lib.rs Line 592 in a399db0 |
| /// split-screen game. | ||
| #[derive(Clone, Copy, Component, Default, Debug, Reflect)] | ||
| #[reflect(Clone, Component, Default)] | ||
| pub struct PrimaryCamera; |
There was a problem hiding this comment.
I'm nervous about the introduction of this. My understanding is that per-camera shadow maps is likely to always be too expensive.
This design feels fundamentally wrong for split-screen games, even though it works for many others.
Could we change the semantics here to allow for tagging multiple cameras, and then taking the union of shadows that should be seen by any of these cameras? We'd need to rename this component obviously. We can even bound the max number of cameras used for this comfortably: 4 or 8 is plenty.
There was a problem hiding this comment.
That won't work for GPU driven because you would have a sequential loop over an array for every mesh in the shader. The right solution is has_own_point_and_spot_light_shadow_maps, which is something I'll implement after 0.19.
There was a problem hiding this comment.
In other words, per-camera shadow maps aren't necessarily too expensive. It really depends on the scene. There's a general feeling that they're too expensive by default, but we should support them at least on an opt-in basis. cart and Griffin disagree as to whether they should be enabled by default; I'm neutral on that question and simply think we should support both.
| /// Placing this component on a [`Camera`] makes that camera the primary one for | ||
| /// the purposes of shadow mapping of point and spot lights. | ||
| /// | ||
| /// The exact algorithm that Bevy uses to determine a primary camera is as |
There was a problem hiding this comment.
I really like this fallback strategy: good defaults with manual control.
IIUC this isn't absolutely necessary because that filter isnt excessively filtering out meshes, and the logic inside this PR will cover any meshes / shadows that absolutely should be culled. It would be redundant to put the logic there too (but i could understand wanting the logic to be consistent for consistency's sake). This is just my understanding of the code though, may be flawed! |
|
That check is redundant (or would be if we had proper CPU culling for shadow maps--but I'll do that in a followup), but I would still like to keep it because it avoids overhead of render world extraction for meshes that aren't going to be visible anyway. Extraction from the main world to the render world can be expensive and it's nice to be able to avoid it when we can. |
camera; fix CPU visibility range computation; pick the lowest entity ID in the case of a tie
Yes, I changed this. |
|
I may miss some context here but consistent in which way? |
|
@urben1680 I mean consistent if you never spawn or despawn cameras, even if you do all sorts of other things to change archetypes and so forth. |
…evyengine#24343) This PR undoes the revert of bevyengine#23115 that was done in bevyengine#24252. As part of doing so, it makes GPU-driven visibility range evaluation use the same semantics, introduced in bevyengine#24289, as CPU-driven visibility range evaluation. The first commit in this PR is the un-revert, and the second commit is the change to use the new machinery bevyengine#24289. This means that you can simply review the second commit (which is very short). To test, run the `visibility_range` example with `--no-cpu-culling`, and use the WASD keys to move behind the flight helmet and zoom out, while closely watching the shadow. Note that the shadow now properly reflects the LOD of the model.
|
This causes a panic on startup when using |
… rendering. (bevyengine#24359) PR bevyengine#24289 introduced the `RenderShadowLodOrigin` resource, which is used to construct the `View` GPU resource. Because shadow maps are a PBR concept, that resource is only constructed if the PBR plugin is present. But we share the `View` structure between the 2D and 3D paths. Thus, when constructing the `View` structure, we should gracefully handle the potential absence of the `RenderShadowLodOrigin` resource. This PR does that. Closes bevyengine#24352.
Right now, visibility ranges are always resolved relative to the view. This is incorrect for shadow maps in two ways: 1. Visibility ranges for meshes in directional light shadow maps should be resolved relative to the camera that the cascades are associated with. 2. Visibility ranges for meshes in point and spot light shadow maps should be resolved relative to *some* camera. To properly solve (2), this commit introduces the notion of a *shadow LOD origin*. The shadow LOD origin is the point that visibility ranges are relative to, when rendering views not associated with any camera. Point and spot light shadow maps are currently not associated with a camera, and therefore we need this extra notion in order to properly evaluate visibility ranges. (As a follow-up, we should introduce the notion of *own shadow maps*, which will allow each camera to have separate shadow maps for point and spot lights. That feature is however out of scope for *this* patch, which simply seeks to make the existing semantics consistent.) A new component, `ShadowLodOrigin`, has been added, which allows the developer to customize the shadow LOD origin. In the absence of this component, this PR implements a simple heuristic to determine the shadow LOD origin: to prefer an origin that coincides with cameras that render to a window. This heuristic should suffice in the vast majority of cases, so developers will rarely have to manually use the `ShadowLodOrigin` component. A new field, `lod_view_world_position`, has been added to `View` to supply the position of the shadow LOD origin to the GPU. This is much simpler than introducing a new uniform or using immediates, as bevyengine#24197 tried to do. This commit is the proper fix for bevyengine#23991. PR bevyengine#24252 attempted to fix this problem by reverting bevyengine#23115. However, this didn't actually fix the issue, because the semantics were still inconsistent. This commit constitutes the correct fix for the issue. I verified that, after un-reverting bevyengine#23115 on top of this patch and modifying it to use the new `lod_view_world_position`, that the issue reported in bevyengine#23991 disappears.
…evyengine#24343) This PR undoes the revert of bevyengine#23115 that was done in bevyengine#24252. As part of doing so, it makes GPU-driven visibility range evaluation use the same semantics, introduced in bevyengine#24289, as CPU-driven visibility range evaluation. The first commit in this PR is the un-revert, and the second commit is the change to use the new machinery bevyengine#24289. This means that you can simply review the second commit (which is very short). To test, run the `visibility_range` example with `--no-cpu-culling`, and use the WASD keys to move behind the flight helmet and zoom out, while closely watching the shadow. Note that the shadow now properly reflects the LOD of the model.
… rendering. (bevyengine#24359) PR bevyengine#24289 introduced the `RenderShadowLodOrigin` resource, which is used to construct the `View` GPU resource. Because shadow maps are a PBR concept, that resource is only constructed if the PBR plugin is present. But we share the `View` structure between the 2D and 3D paths. Thus, when constructing the `View` structure, we should gracefully handle the potential absence of the `RenderShadowLodOrigin` resource. This PR does that. Closes bevyengine#24352.
Right now, visibility ranges are always resolved relative to the view. This is incorrect for shadow maps in two ways: 1. Visibility ranges for meshes in directional light shadow maps should be resolved relative to the camera that the cascades are associated with. 2. Visibility ranges for meshes in point and spot light shadow maps should be resolved relative to *some* camera. To properly solve (2), this commit introduces the notion of a *shadow LOD origin*. The shadow LOD origin is the point that visibility ranges are relative to, when rendering views not associated with any camera. Point and spot light shadow maps are currently not associated with a camera, and therefore we need this extra notion in order to properly evaluate visibility ranges. (As a follow-up, we should introduce the notion of *own shadow maps*, which will allow each camera to have separate shadow maps for point and spot lights. That feature is however out of scope for *this* patch, which simply seeks to make the existing semantics consistent.) A new component, `ShadowLodOrigin`, has been added, which allows the developer to customize the shadow LOD origin. In the absence of this component, this PR implements a simple heuristic to determine the shadow LOD origin: to prefer an origin that coincides with cameras that render to a window. This heuristic should suffice in the vast majority of cases, so developers will rarely have to manually use the `ShadowLodOrigin` component. A new field, `lod_view_world_position`, has been added to `View` to supply the position of the shadow LOD origin to the GPU. This is much simpler than introducing a new uniform or using immediates, as #24197 tried to do. This commit is the proper fix for #23991. PR #24252 attempted to fix this problem by reverting #23115. However, this didn't actually fix the issue, because the semantics were still inconsistent. This commit constitutes the correct fix for the issue. I verified that, after un-reverting #23115 on top of this patch and modifying it to use the new `lod_view_world_position`, that the issue reported in #23991 disappears.
…24343) This PR undoes the revert of #23115 that was done in #24252. As part of doing so, it makes GPU-driven visibility range evaluation use the same semantics, introduced in #24289, as CPU-driven visibility range evaluation. The first commit in this PR is the un-revert, and the second commit is the change to use the new machinery #24289. This means that you can simply review the second commit (which is very short). To test, run the `visibility_range` example with `--no-cpu-culling`, and use the WASD keys to move behind the flight helmet and zoom out, while closely watching the shadow. Note that the shadow now properly reflects the LOD of the model.
… rendering. (#24359) PR #24289 introduced the `RenderShadowLodOrigin` resource, which is used to construct the `View` GPU resource. Because shadow maps are a PBR concept, that resource is only constructed if the PBR plugin is present. But we share the `View` structure between the 2D and 3D paths. Thus, when constructing the `View` structure, we should gracefully handle the potential absence of the `RenderShadowLodOrigin` resource. This PR does that. Closes #24352.
Right now, visibility ranges are always resolved relative to the view. This is incorrect for shadow maps in two ways:
Visibility ranges for meshes in directional light shadow maps should be resolved relative to the camera that the cascades are associated with.
Visibility ranges for meshes in point and spot light shadow maps should be resolved relative to some camera.
To properly solve (2), this commit introduces the notion of a shadow LOD origin. The shadow LOD origin is the point that visibility ranges are relative to, when rendering views not associated with any camera. Point and spot light shadow maps are currently not associated with a camera, and therefore we need this extra notion in order to properly evaluate visibility ranges.
(As a follow-up, we should introduce the notion of own shadow maps, which will allow each camera to have separate shadow maps for point and spot lights. That feature is however out of scope for this patch, which simply seeks to make the existing semantics consistent.)
A new component,
ShadowLodOrigin, has been added, which allows the developer to customize the shadow LOD origin. In the absence of this component, this PR implements a simple heuristic to determine the shadow LOD origin: to prefer an origin that coincides with cameras that render to a window. This heuristic should suffice in the vast majority of cases, so developers will rarely have to manually use theShadowLodOrigincomponent.A new field,
lod_view_world_position, has been added toViewto supply the position of the shadow LOD origin to the GPU. This is much simpler than introducing a new uniform or using immediates, as #24197 tried to do.This commit is the proper fix for #23991. PR #24252 attempted to fix this problem by reverting #23115. However, this didn't actually fix the issue, because the semantics were still inconsistent. This commit constitutes the correct fix for the issue. I verified that, after un-reverting #23115 on top of this patch and modifying it to use the new
lod_view_world_position, that the issue reported in #23991 disappears.