Opt-in-Per-Camera Pointlight and Spotlight Shadow Maps#24625
Conversation
|
Hm there appears to an issue with rendering when there are multiple PointLights since my index math is messed up on the shader code side. Will figure it out |
3d1be2a to
ad3d74b
Compare
|
Awesome! I’ll review this shortly. |
pcwalton
left a comment
There was a problem hiding this comment.
Partial review only—I’ll have to finish this tomorrow—but this is really awesome and fiddly work. Thank you for doing it! It really makes our story around shadow maps a lot better.
| /// will generate 6 additional shadow maps per camera that opts to have its own shadow maps. | ||
| /// | ||
| /// Defaults to `false`. | ||
| pub has_own_shadow_maps: bool, |
There was a problem hiding this comment.
has_own_point_and_spot_light_shadow_maps might be better, if verbose, since views always have own directional shadow maps.
| pub mip_bias: f32, | ||
| pub frame_count: u32, | ||
| /// This offset is used to fetch the correct point or spot shadow map to use for this view. | ||
| /// This is used to accommodates views that may be configured to have their own point or spot shadow maps. |
There was a problem hiding this comment.
typo: should be “to accommodate”
| /// This must be multiplied to the initial index. | ||
| pub point_spot_shadow_map_index_mult_offset: u32, | ||
| /// This offset is used to fetch the correct point or spot shadow map to use for this view. | ||
| /// This is used to accommodates views that may be configured to have their own point or spot shadow maps. |
| /// This is used to accommodates views that may be configured to have their own point or spot shadow maps. | ||
| /// This represents the additive index of the point/spot shadow map that this view should use. | ||
| /// This must be added to the index after the multiplicative offset has been applied. | ||
| pub point_spot_shadow_map_index_add_offset: u32, |
There was a problem hiding this comment.
I think I’d prefer calling this something like point_spot_shadow_map_index.
| /// This represents the total number of shadow maps (not counting shadow maps of different face indices) | ||
| /// that have been generated to be utilized by all possible views. | ||
| /// This must be multiplied to the initial index. | ||
| pub point_spot_shadow_map_index_mult_offset: u32, |
There was a problem hiding this comment.
I think I’d prefer calling this something like point_spot_shadow_map_count.
| #[derive(Component)] | ||
| pub struct SpotLightShadowViewEntity { | ||
| /// The spotlight shadow view associated with a camera that has opted | ||
| /// in to its own shadow maps. |
| Some((entity, MainEntity::from(main_entity))) | ||
| }) | ||
| .collect(); | ||
| if views_count - point_spot_shadow_aux_entities.len() > 0 { |
There was a problem hiding this comment.
| if views_count - point_spot_shadow_aux_entities.len() > 0 { | |
| if views_count > point_spot_shadow_aux_entities.len() { |
| let insert_shadow_map = match auxiliary_entity { | ||
| Some((entity, _)) => point_and_spot_light_view_entities.1.get(entity).is_none(), | ||
| None => point_and_spot_light_view_entities.0.is_empty(), | ||
| }; |
There was a problem hiding this comment.
| let insert_shadow_map = match auxiliary_entity { | |
| Some((entity, _)) => point_and_spot_light_view_entities.1.get(entity).is_none(), | |
| None => point_and_spot_light_view_entities.0.is_empty(), | |
| }; | |
| match auxiliary_entity { | |
| Some((entity, _)) => { | |
| if point_and_spot_light_view_entities.1.get(entity).is_some() { | |
| continue; | |
| } | |
| } | |
| None => { | |
| if !point_and_spot_light_view_entities.0.is_empty() { | |
| continue; | |
| } | |
| } | |
| } |
And then remove the if insert_shadow_map check below, eliminating some rightward drift.
| maybe_layers, | ||
| no_indirect_drawing, | ||
| maybe_ambient_override, | ||
| _extracted_camera, |
There was a problem hiding this comment.
Remove the _ here since the variable is now used.
| /// Creates six point shadow maps for a `RetainedViewEntity` identified by the `light_main_entity` | ||
| /// and the six cube face rotation indices. These shadow maps are shared across | ||
| /// all cameras. | ||
| /// Creates six point shadow map for a `RetainedViewEntity` identified by the `light_main_entity`, |
There was a problem hiding this comment.
| /// Creates six point shadow map for a `RetainedViewEntity` identified by the `light_main_entity`, | |
| /// Creates six point shadow maps for a `RetainedViewEntity` identified by the `light_main_entity`, |
Objective
This is intended to be that follow-up if I understood the intention correctly 😅
This idea has also floated around on Discord, with an emphasis on this being configurable, so I preserved that desire in this PR.
Solution
A new property,
has_own_shadow_maps, has been added toCamera. By default, this is set tofalse, preserving existing behavior.If a camera requests its own shadow maps, new depth attachments and
ShadowViews are created for these additional views inprepare_lightsinlight.rs, which is where the meat of this PR is.prepare_lightsalso manages what happens if the light itself changes or the views that opt in change. There’s also an added optimization that if all the cameras request their own shadow maps, the view-agnostic pointlight/spotlight shadow map is removed/not created.The shader code changes are to ensure the new shadow maps are actually being used. Two new indices offset,
point_spot_shadow_map_index_mult_offsetand…_add_offset, are used to correctly index into the correct shadow maps. A lot of the wgsl code changes are to refactor a variable fromlight_idtoarray_index, as this more accurately describes its use and it matches the corresponding shadow sampling code for spotlight shadows. (actively soliciting for less verbose names 😆 )Testing
For correctness:
cargo run --example testbed_3d --features=“area_light_luts” -- renderlayersI tested with the render_layers (with the changes I put in for the light to be on the 3 render layers). It works as expected now!
I tested both a
PointLightand aSpotLight(have to change the Spotlight transform to add a looking_at:Transform::from_xyz(4.0, 8.0, 4.0).looking_at(Vec3::new(3.0, 0.5, 0.0), Vec3::Y)))I’ll probably run the bevy example runner against this PR to ensure there are no correctness regressions otherwise.
I have not done any performance testing (nor know if it’s necesssary). I don’t know if there is a performance regression in the regular “no camera has their own shadow map” case or if we should be concerned about that. If someone can help with this, I’d be indebted.
Showcase
The render layers scene in the 3d testbed with a PointLight now after the changes in this PR:

The same scene with a SpotLight instead described above in Testing:
