Skip to content

Commit 638c5dd

Browse files
authored
Fix: Update the ExtractedView & Frustum when the ExtractedPointLight has changed (#24038)
# Objective - Fixes #23997 for `spotlight` (`rect_light` fixed with #24024 already) - The spotlight example changes the transform of each spotlight, but the `ExtractedView` and the `Frustum` for the spotlight never updates, hence why the light doesn’t look complete for subsequent frames. ## Solution - For Spot lights (and Point lights since those were also affected), update the `ExtractedView` and the `Frustum` if its corresponding `ExtractedPointLight` has changed. - AFAIK this isn’t needed for Directional lights because `ExtractedView`s aren’t re-used — they’re made anew in `prepare_lights` ## Testing - `cargo run --example spotlight` works as desired. - `cargo run --example async_channel_pattern` works as desired (for PointLight testing) - @Zeophlite assist with the example runner (was done before the frusta change though)
1 parent dfb904c commit 638c5dd

2 files changed

Lines changed: 80 additions & 2 deletions

File tree

crates/bevy_pbr/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -439,7 +439,7 @@ impl Plugin for PbrPlugin {
439439
shared_shadow_pass::<EARLY_SHADOW_PASS>
440440
.after(early_prepass_build_indirect_parameters)
441441
.before(early_downsample_depth)
442-
.before(per_view_shadow_pass::<LATE_SHADOW_PASS>),
442+
.before(shared_shadow_pass::<LATE_SHADOW_PASS>),
443443
shared_shadow_pass::<LATE_SHADOW_PASS>
444444
.after(late_prepass_build_indirect_parameters)
445445
.before(main_build_indirect_parameters)

crates/bevy_pbr/src/render/light.rs

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1004,14 +1004,28 @@ pub fn prepare_lights(
10041004
Local<bool>,
10051005
Local<HashSet<RetainedViewEntity>>,
10061006
),
1007-
(mut point_lights, directional_lights, rect_lights, mut directional_light_view_entities): (
1007+
(
1008+
mut point_lights,
1009+
changed_point_lights,
1010+
directional_lights,
1011+
rect_lights,
1012+
mut directional_light_view_entities,
1013+
): (
10081014
Query<(
10091015
Entity,
10101016
&MainEntity,
10111017
&ExtractedPointLight,
10121018
&mut PointAndSpotLightViewEntities,
10131019
AnyOf<(&CubemapFrusta, &Frustum)>,
10141020
)>,
1021+
Query<
1022+
(),
1023+
Or<(
1024+
Changed<ExtractedPointLight>,
1025+
Changed<CubemapFrusta>,
1026+
Changed<Frustum>,
1027+
)>,
1028+
>,
10151029
Query<(Entity, &MainEntity, &ExtractedDirectionalLight)>,
10161030
Query<(Entity, &MainEntity, &ExtractedRectLight)>,
10171031
Query<&mut DirectionalLightViewEntities>,
@@ -1548,6 +1562,41 @@ pub fn prepare_lights(
15481562
}
15491563

15501564
point_and_spot_light_view_entities.0 = light_view_entities;
1565+
} else if changed_point_lights.get(*light_entity).is_ok() {
1566+
// If the point light was changed, update the `ExtractedView` only.
1567+
let view_translation = GlobalTransform::from_translation(light.transform.translation());
1568+
let cube_face_projection = Mat4::perspective_infinite_reverse_rh(
1569+
core::f32::consts::FRAC_PI_2,
1570+
1.0,
1571+
light.shadow_map_near_z,
1572+
);
1573+
for (face_index, (view_rotation, frustum)) in cube_face_rotations
1574+
.iter()
1575+
.zip(&point_light_frusta.unwrap().frusta)
1576+
.enumerate()
1577+
{
1578+
let view_light_entity = point_and_spot_light_view_entities.0[face_index];
1579+
let retained_view_entity =
1580+
RetainedViewEntity::new(*light_main_entity, None, face_index as u32);
1581+
commands.entity(view_light_entity).insert((
1582+
ExtractedView {
1583+
retained_view_entity,
1584+
viewport: UVec4::new(
1585+
0,
1586+
0,
1587+
point_light_shadow_map.size as u32,
1588+
point_light_shadow_map.size as u32,
1589+
),
1590+
world_from_view: view_translation * *view_rotation,
1591+
clip_from_world: None,
1592+
clip_from_view: cube_face_projection,
1593+
target_format: CORE_3D_DEPTH_FORMAT,
1594+
color_grading: Default::default(),
1595+
invert_culling: false,
1596+
},
1597+
*frustum,
1598+
));
1599+
}
15511600
}
15521601

15531602
// Initialize the shadow render phases. We have to do this even if we've
@@ -1658,6 +1707,35 @@ pub fn prepare_lights(
16581707
}
16591708

16601709
point_and_spot_light_view_entities.0 = vec![view_light_entity];
1710+
} else if changed_point_lights.get(*light_entity).is_ok() {
1711+
// If the spot light was changed, update the `ExtractedView` only.
1712+
let spot_world_from_view = spot_light_world_from_view(&light.transform);
1713+
let spot_world_from_view = spot_world_from_view.into();
1714+
1715+
let angle = light.spot_light_angles.expect("lights should be sorted so that \
1716+
[point_light_count..point_light_count + spot_light_shadow_maps_count] are spot lights").1;
1717+
let spot_projection = spot_light_clip_from_view(angle, light.shadow_map_near_z);
1718+
1719+
// There should be only one `view_light_entity` for spotlights.
1720+
let view_light_entity = point_and_spot_light_view_entities.0[0];
1721+
commands.entity(view_light_entity).insert((
1722+
ExtractedView {
1723+
retained_view_entity,
1724+
viewport: UVec4::new(
1725+
0,
1726+
0,
1727+
directional_light_shadow_map.size as u32,
1728+
directional_light_shadow_map.size as u32,
1729+
),
1730+
world_from_view: spot_world_from_view,
1731+
clip_from_view: spot_projection,
1732+
clip_from_world: None,
1733+
target_format: CORE_3D_DEPTH_FORMAT,
1734+
color_grading: Default::default(),
1735+
invert_culling: false,
1736+
},
1737+
*spot_light_frustum.unwrap(),
1738+
));
16611739
}
16621740

16631741
shadow_render_phases.prepare_for_new_frame(

0 commit comments

Comments
 (0)