From df26bc338701dd77f688328c71e0744f8763e8c9 Mon Sep 17 00:00:00 2001 From: HugoPeters1024 Date: Tue, 25 Mar 2025 05:48:49 +0100 Subject: [PATCH] bugfix(frustra of point lights were not recalculated when a camera changes) (#18519) # Objective - Fixes https://github.com/bevyengine/bevy/issues/11682 ## Solution - https://github.com/bevyengine/bevy/pull/4086 introduced an optimization to not do redundant calculations, but did not take into account changes to the resource `global_lights`. I believe that my patch includes the optimization benefit but adds the required nuance to fix said bug. ## Testing The example originally given by [@kirillsurkov](https://github.com/kirillsurkov) and then updated by me to bevy 15.3 here: https://github.com/bevyengine/bevy/issues/11682#issuecomment-2746287416 will not have shadows without this patch: ```rust use bevy::prelude::*; #[derive(Resource)] struct State { x: f32, } fn main() { App::new() .add_plugins(DefaultPlugins) .add_systems(Startup, setup) .add_systems(Update, update) .insert_resource(State { x: -40.0 }) .run(); } fn setup( mut commands: Commands, mut meshes: ResMut>, mut materials: ResMut>, ) { commands.spawn(( Mesh3d(meshes.add(Circle::new(4.0))), MeshMaterial3d(materials.add(Color::WHITE)), )); commands.spawn(( Mesh3d(meshes.add(Cuboid::new(1.0, 1.0, 1.0))), MeshMaterial3d(materials.add(Color::linear_rgb(0.0, 1.0, 0.0))), )); commands.spawn(( PointLight { shadows_enabled: true, ..default() }, Transform::from_xyz(4.0, 8.0, 4.0), )); commands.spawn(Camera3d::default()); } fn update(mut state: ResMut, mut camera: Query<&mut Transform, With>) { let mut camera = camera.single_mut().unwrap(); let t = Vec3::new(state.x, 0.0, 10.0); camera.translation = t; camera.look_at(t - Vec3::Z, Vec3::Y); state.x = 0.0; } ``` --------- Co-authored-by: Alice Cecile Co-authored-by: JMS55 <47158642+JMS55@users.noreply.github.com> --- crates/bevy_pbr/src/light/mod.rs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/crates/bevy_pbr/src/light/mod.rs b/crates/bevy_pbr/src/light/mod.rs index 3f57464e21..bc893268dc 100644 --- a/crates/bevy_pbr/src/light/mod.rs +++ b/crates/bevy_pbr/src/light/mod.rs @@ -564,9 +564,13 @@ pub fn update_directional_light_frusta( // NOTE: Run this after assign_lights_to_clusters! pub fn update_point_light_frusta( global_lights: Res, - mut views: Query< - (Entity, &GlobalTransform, &PointLight, &mut CubemapFrusta), - Or<(Changed, Changed)>, + mut views: Query<(Entity, &GlobalTransform, &PointLight, &mut CubemapFrusta)>, + changed_lights: Query< + Entity, + ( + With, + Or<(Changed, Changed)>, + ), >, ) { let view_rotations = CUBE_MAP_FACES @@ -575,6 +579,12 @@ pub fn update_point_light_frusta( .collect::>(); for (entity, transform, point_light, mut cubemap_frusta) in &mut views { + // If this light hasn't changed, and neither has the set of global_lights, + // then we can skip this calculation. + if !global_lights.is_changed() && !changed_lights.contains(entity) { + continue; + } + // The frusta are used for culling meshes to the light for shadow mapping // so if shadow mapping is disabled for this light, then the frusta are // not needed.