From 713735b855f01e764121121c77f073c906f60c99 Mon Sep 17 00:00:00 2001 From: Adrian Graur Date: Thu, 27 Mar 2025 13:33:02 +0000 Subject: [PATCH 1/8] Signed-off-by: Adrian Graur Fix #18000: Shadows do not respect render layers Shadows of objects are still rendered even when camera is rendering a different render layer. This happens because `queue_shadows` does not use the camera render layers when filtering visible entities. To fix it, i just exported the Mesh render layers and filtered them according to the camera render layers. --- crates/bevy_pbr/src/components.rs | 3 ++- crates/bevy_pbr/src/render/light.rs | 27 +++++++++++++++++++-------- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/crates/bevy_pbr/src/components.rs b/crates/bevy_pbr/src/components.rs index d70da7cd9509d..7c252b8a025a2 100644 --- a/crates/bevy_pbr/src/components.rs +++ b/crates/bevy_pbr/src/components.rs @@ -4,6 +4,7 @@ use bevy_ecs::entity::{hash_map::EntityHashMap, Entity}; use bevy_ecs::reflect::ReflectComponent; use bevy_reflect::{std_traits::ReflectDefault, Reflect}; use bevy_render::sync_world::MainEntity; +use bevy_render::view::RenderLayers; /// Collection of mesh entities visible for 3D lighting. /// /// This component contains all mesh entities visible from the current light view. @@ -19,7 +20,7 @@ pub struct VisibleMeshEntities { #[reflect(Component, Debug, Default, Clone)] pub struct RenderVisibleMeshEntities { #[reflect(ignore, clone)] - pub entities: Vec<(Entity, MainEntity)>, + pub entities: Vec<(Entity, MainEntity, Option)>, } #[derive(Component, Clone, Debug, Default, Reflect)] diff --git a/crates/bevy_pbr/src/render/light.rs b/crates/bevy_pbr/src/render/light.rs index a770a83bca132..4cf95af395689 100644 --- a/crates/bevy_pbr/src/render/light.rs +++ b/crates/bevy_pbr/src/render/light.rs @@ -265,6 +265,7 @@ pub fn extract_lights( >, >, mapper: Extract>, + mesh_layers_query: Extract>, mut previous_point_lights_len: Local, mut previous_spot_lights_len: Local, ) { @@ -306,7 +307,7 @@ pub fn extract_lights( let render_cubemap_visible_entities = RenderCubemapVisibleEntities { data: cubemap_visible_entities .iter() - .map(|v| create_render_visible_mesh_entities(&mapper, v)) + .map(|v| create_render_visible_mesh_entities(&mapper, v, &mesh_layers_query)) .collect::>() .try_into() .unwrap(), @@ -366,7 +367,7 @@ pub fn extract_lights( continue; } let render_visible_entities = - create_render_visible_mesh_entities(&mapper, visible_entities); + create_render_visible_mesh_entities(&mapper, visible_entities, &mesh_layers_query); let texel_size = 2.0 * ops::tan(spot_light.outer_angle) / directional_light_shadow_map.size as f32; @@ -458,7 +459,7 @@ pub fn extract_lights( cascade_visible_entities.insert( entity, v.iter() - .map(|v| create_render_visible_mesh_entities(&mapper, v)) + .map(|v| create_render_visible_mesh_entities(&mapper, v, &mesh_layers_query)) .collect(), ); } else { @@ -503,13 +504,18 @@ pub fn extract_lights( fn create_render_visible_mesh_entities( mapper: &Extract>, visible_entities: &VisibleMeshEntities, + mesh_layers_query: &Extract>, ) -> RenderVisibleMeshEntities { RenderVisibleMeshEntities { entities: visible_entities .iter() .map(|e| { let render_entity = mapper.get(*e).unwrap_or(Entity::PLACEHOLDER); - (render_entity, MainEntity::from(*e)) + + // Extract RenderLayers from the mesh entity + let mesh_render_layers = mesh_layers_query.get(*e).ok().cloned(); + + (render_entity, MainEntity::from(*e), mesh_render_layers) }) .collect(), } @@ -1808,7 +1814,7 @@ pub fn specialize_shadows( .entry(extracted_view_light.retained_view_entity) .or_default(); - for (_, visible_entity) in visible_entities.iter().copied() { + for (_, visible_entity, _) in visible_entities.iter().cloned() { let Some(material_asset_id) = render_material_instances.get(&visible_entity) else { continue; }; @@ -1910,7 +1916,7 @@ pub fn queue_shadows( mut shadow_render_phases: ResMut>, gpu_preprocessing_support: Res, mesh_allocator: Res, - view_lights: Query<(Entity, &ViewLightEntities), With>, + view_lights: Query<(Entity, &ViewLightEntities, &RenderLayers), With>, view_light_entities: Query<(&LightEntity, &ExtractedView)>, point_light_entities: Query<&RenderCubemapVisibleEntities, With>, directional_light_entities: Query< @@ -1923,7 +1929,7 @@ pub fn queue_shadows( M::Data: PartialEq + Eq + Hash + Clone, { let draw_shadow_mesh = shadow_draw_functions.read().id::>(); - for (entity, view_lights) in &view_lights { + for (entity, view_lights, camera_layers) in &view_lights { for view_light_entity in view_lights.lights.iter().copied() { let Ok((light_entity, extracted_view_light)) = view_light_entities.get(view_light_entity) @@ -1966,13 +1972,18 @@ pub fn queue_shadows( .expect("Failed to get spot light visible entities"), }; - for (entity, main_entity) in visible_entities.iter().copied() { + for (entity, main_entity, mesh_render_layers) in visible_entities.iter().cloned() { let Some((current_change_tick, pipeline_id)) = view_specialized_material_pipeline_cache.get(&main_entity) else { continue; }; + // Skip the entity if it's not in the camera render layers. + if !camera_layers.intersects(mesh_render_layers.as_ref().unwrap_or(&RenderLayers::default())) { + continue; + } + // Skip the entity if it's cached in a bin and up to date. if shadow_phase.validate_cached_entity(main_entity, *current_change_tick) { continue; From b4300fc2c4f199ee6a756e62518edc9b7d193664 Mon Sep 17 00:00:00 2001 From: Adrian Graur Date: Tue, 22 Apr 2025 18:47:21 +0100 Subject: [PATCH 2/8] Fixed fix for #18000: Shadows do not respect render layers Filter was removing shadows of objects without any layers. Changed code to just filter if mesh has RenderLayers assigned. Signed-off-by: Adrian Graur --- crates/bevy_pbr/src/render/light.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/crates/bevy_pbr/src/render/light.rs b/crates/bevy_pbr/src/render/light.rs index 4cf95af395689..4be45c57bd743 100644 --- a/crates/bevy_pbr/src/render/light.rs +++ b/crates/bevy_pbr/src/render/light.rs @@ -1916,7 +1916,7 @@ pub fn queue_shadows( mut shadow_render_phases: ResMut>, gpu_preprocessing_support: Res, mesh_allocator: Res, - view_lights: Query<(Entity, &ViewLightEntities, &RenderLayers), With>, + view_lights: Query<(Entity, &ViewLightEntities, Option<&RenderLayers>), With>, view_light_entities: Query<(&LightEntity, &ExtractedView)>, point_light_entities: Query<&RenderCubemapVisibleEntities, With>, directional_light_entities: Query< @@ -1980,8 +1980,13 @@ pub fn queue_shadows( }; // Skip the entity if it's not in the camera render layers. - if !camera_layers.intersects(mesh_render_layers.as_ref().unwrap_or(&RenderLayers::default())) { - continue; + match (camera_layers, mesh_render_layers) { + (Some(camera_layers), Some(mesh_layers)) => { + if !camera_layers.intersects(&mesh_layers) { + continue; + } + } + _ => {} } // Skip the entity if it's cached in a bin and up to date. From fd1a1d19cb052f1d28085c33c987f9f1a33b81ed Mon Sep 17 00:00:00 2001 From: Adrian Graur Date: Tue, 22 Apr 2025 19:30:33 +0100 Subject: [PATCH 3/8] fix: fixed formating Signed-off-by: Adrian Graur --- crates/bevy_pbr/src/render/light.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/bevy_pbr/src/render/light.rs b/crates/bevy_pbr/src/render/light.rs index af03a5c3796c4..ec34d29828657 100644 --- a/crates/bevy_pbr/src/render/light.rs +++ b/crates/bevy_pbr/src/render/light.rs @@ -459,7 +459,9 @@ pub fn extract_lights( cascade_visible_entities.insert( entity, v.iter() - .map(|v| create_render_visible_mesh_entities(&mapper, v, &mesh_layers_query)) + .map(|v| { + create_render_visible_mesh_entities(&mapper, v, &mesh_layers_query) + }) .collect(), ); } else { @@ -1814,7 +1816,6 @@ pub fn specialize_shadows( .entry(extracted_view_light.retained_view_entity) .or_default(); - for (_, visible_entity, _) in visible_entities.iter().cloned() { let Some(material_instances) = render_material_instances.instances.get(&visible_entity) @@ -1827,7 +1828,6 @@ pub fn specialize_shadows( let Some(mesh_instance) = render_mesh_instances.render_mesh_queue_data(visible_entity) else { - continue; }; let entity_tick = entity_specialization_ticks.get(&visible_entity).unwrap(); From d4fa841f93e21311c5e59f8ef1b8e6d8f3be79f4 Mon Sep 17 00:00:00 2001 From: Adrian Graur Date: Tue, 22 Apr 2025 21:02:55 +0100 Subject: [PATCH 4/8] fix: match warning Signed-off-by: Adrian Graur --- crates/bevy_pbr/src/render/light.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/crates/bevy_pbr/src/render/light.rs b/crates/bevy_pbr/src/render/light.rs index ec34d29828657..1fd86b65423a5 100644 --- a/crates/bevy_pbr/src/render/light.rs +++ b/crates/bevy_pbr/src/render/light.rs @@ -1987,13 +1987,12 @@ pub fn queue_shadows( }; // Skip the entity if it's not in the camera render layers. - match (camera_layers, mesh_render_layers) { - (Some(camera_layers), Some(mesh_layers)) => { - if !camera_layers.intersects(&mesh_layers) { - continue; - } + if let (Some(camera_layers), Some(mesh_layers)) = + (camera_layers, mesh_render_layers) + { + if !camera_layers.intersects(&mesh_layers) { + continue; } - _ => {} } // Skip the entity if it's cached in a bin and up to date. From 340dac761cf440f8de9f1d1c7690a028e12f551d Mon Sep 17 00:00:00 2001 From: Adrian Graur Date: Tue, 6 May 2025 23:31:39 +0100 Subject: [PATCH 5/8] Changed render layers location to RenderMeshInstanceShared. Signed-off-by: Adrian Graur --- crates/bevy_pbr/src/components.rs | 3 +- crates/bevy_pbr/src/render/light.rs | 48 ++++++++++++----------------- crates/bevy_pbr/src/render/mesh.rs | 11 +++++++ 3 files changed, 32 insertions(+), 30 deletions(-) diff --git a/crates/bevy_pbr/src/components.rs b/crates/bevy_pbr/src/components.rs index e1c45a045665f..fca31b3b034af 100644 --- a/crates/bevy_pbr/src/components.rs +++ b/crates/bevy_pbr/src/components.rs @@ -4,7 +4,6 @@ use bevy_ecs::entity::{Entity, EntityHashMap}; use bevy_ecs::reflect::ReflectComponent; use bevy_reflect::{std_traits::ReflectDefault, Reflect}; use bevy_render::sync_world::MainEntity; -use bevy_render::view::RenderLayers; /// Collection of mesh entities visible for 3D lighting. /// /// This component contains all mesh entities visible from the current light view. @@ -20,7 +19,7 @@ pub struct VisibleMeshEntities { #[reflect(Component, Debug, Default, Clone)] pub struct RenderVisibleMeshEntities { #[reflect(ignore, clone)] - pub entities: Vec<(Entity, MainEntity, Option)>, + pub entities: Vec<(Entity, MainEntity)>, } #[derive(Component, Clone, Debug, Default, Reflect)] diff --git a/crates/bevy_pbr/src/render/light.rs b/crates/bevy_pbr/src/render/light.rs index 1fd86b65423a5..302808666990d 100644 --- a/crates/bevy_pbr/src/render/light.rs +++ b/crates/bevy_pbr/src/render/light.rs @@ -265,7 +265,6 @@ pub fn extract_lights( >, >, mapper: Extract>, - mesh_layers_query: Extract>, mut previous_point_lights_len: Local, mut previous_spot_lights_len: Local, ) { @@ -307,7 +306,7 @@ pub fn extract_lights( let render_cubemap_visible_entities = RenderCubemapVisibleEntities { data: cubemap_visible_entities .iter() - .map(|v| create_render_visible_mesh_entities(&mapper, v, &mesh_layers_query)) + .map(|v| create_render_visible_mesh_entities(&mapper, v)) .collect::>() .try_into() .unwrap(), @@ -367,7 +366,7 @@ pub fn extract_lights( continue; } let render_visible_entities = - create_render_visible_mesh_entities(&mapper, visible_entities, &mesh_layers_query); + create_render_visible_mesh_entities(&mapper, visible_entities); let texel_size = 2.0 * ops::tan(spot_light.outer_angle) / directional_light_shadow_map.size as f32; @@ -459,9 +458,7 @@ pub fn extract_lights( cascade_visible_entities.insert( entity, v.iter() - .map(|v| { - create_render_visible_mesh_entities(&mapper, v, &mesh_layers_query) - }) + .map(|v| create_render_visible_mesh_entities(&mapper, v)) .collect(), ); } else { @@ -506,18 +503,13 @@ pub fn extract_lights( fn create_render_visible_mesh_entities( mapper: &Extract>, visible_entities: &VisibleMeshEntities, - mesh_layers_query: &Extract>, ) -> RenderVisibleMeshEntities { RenderVisibleMeshEntities { entities: visible_entities .iter() .map(|e| { let render_entity = mapper.get(*e).unwrap_or(Entity::PLACEHOLDER); - - // Extract RenderLayers from the mesh entity - let mesh_render_layers = mesh_layers_query.get(*e).ok().cloned(); - - (render_entity, MainEntity::from(*e), mesh_render_layers) + (render_entity, MainEntity::from(*e)) }) .collect(), } @@ -1816,7 +1808,7 @@ pub fn specialize_shadows( .entry(extracted_view_light.retained_view_entity) .or_default(); - for (_, visible_entity, _) in visible_entities.iter().cloned() { + for (_, visible_entity) in visible_entities.iter().copied() { let Some(material_instances) = render_material_instances.instances.get(&visible_entity) else { @@ -1979,27 +1971,13 @@ pub fn queue_shadows( .expect("Failed to get spot light visible entities"), }; - for (entity, main_entity, mesh_render_layers) in visible_entities.iter().cloned() { + for (entity, main_entity) in visible_entities.iter().copied() { let Some((current_change_tick, pipeline_id)) = view_specialized_material_pipeline_cache.get(&main_entity) else { continue; }; - // Skip the entity if it's not in the camera render layers. - if let (Some(camera_layers), Some(mesh_layers)) = - (camera_layers, mesh_render_layers) - { - if !camera_layers.intersects(&mesh_layers) { - continue; - } - } - - // Skip the entity if it's cached in a bin and up to date. - if shadow_phase.validate_cached_entity(main_entity, *current_change_tick) { - continue; - } - let Some(mesh_instance) = render_mesh_instances.render_mesh_queue_data(main_entity) else { continue; @@ -2011,6 +1989,20 @@ pub fn queue_shadows( continue; } + let mesh_layers = mesh_instance.shared.render_layers.as_ref(); + + // Skip the entity if it's not in the camera render layers. + if let (Some(camera_layers), Some(layers)) = (camera_layers, mesh_layers) { + if !camera_layers.intersects(&layers) { + continue; + } + } + + // Skip the entity if it's cached in a bin and up to date. + if shadow_phase.validate_cached_entity(main_entity, *current_change_tick) { + continue; + } + let Some(material_instance) = render_material_instances.instances.get(&main_entity) else { continue; diff --git a/crates/bevy_pbr/src/render/mesh.rs b/crates/bevy_pbr/src/render/mesh.rs index 4bae79b807417..a764a70c33a4c 100644 --- a/crates/bevy_pbr/src/render/mesh.rs +++ b/crates/bevy_pbr/src/render/mesh.rs @@ -74,6 +74,7 @@ use bevy_render::camera::TemporalJitter; use bevy_render::prelude::Msaa; use bevy_render::sync_world::{MainEntity, MainEntityHashMap}; use bevy_render::view::ExtractedView; +use bevy_render::view::RenderLayers; use bevy_render::RenderSet::PrepareAssets; use bytemuck::{Pod, Zeroable}; use nonmax::{NonMaxU16, NonMaxU32}; @@ -754,6 +755,8 @@ pub struct RenderMeshInstanceShared { pub lightmap_slab_index: Option, /// User supplied tag to identify this mesh instance. pub tag: u32, + /// Render layers that this mesh instance belongs to. + pub render_layers: Option, } /// Information that is gathered during the parallel portion of mesh extraction @@ -843,6 +846,7 @@ impl RenderMeshInstanceShared { tag: Option<&MeshTag>, not_shadow_caster: bool, no_automatic_batching: bool, + render_layers: Option<&RenderLayers>, ) -> Self { let mut mesh_instance_flags = RenderMeshInstanceFlags::empty(); mesh_instance_flags.set(RenderMeshInstanceFlags::SHADOW_CASTER, !not_shadow_caster); @@ -862,6 +866,7 @@ impl RenderMeshInstanceShared { material_bindings_index: default(), lightmap_slab_index: None, tag: tag.map_or(0, |i| **i), + render_layers: render_layers.cloned(), } } @@ -1321,6 +1326,7 @@ pub fn extract_meshes_for_cpu_building( Has, Has, Has, + Option<&RenderLayers>, )>, >, ) { @@ -1340,6 +1346,7 @@ pub fn extract_meshes_for_cpu_building( not_shadow_caster, no_automatic_batching, visibility_range, + render_layers, )| { if !view_visibility.get() { return; @@ -1364,6 +1371,7 @@ pub fn extract_meshes_for_cpu_building( tag, not_shadow_caster, no_automatic_batching, + render_layers, ); let world_from_local = transform.affine(); @@ -1417,6 +1425,7 @@ type GpuMeshExtractionQuery = ( Has, Has, Has, + Option>, ); /// Extracts meshes from the main world into the render world and queues @@ -1542,6 +1551,7 @@ fn extract_mesh_for_gpu_building( not_shadow_caster, no_automatic_batching, visibility_range, + render_layers, ): ::Item<'_>, render_visibility_ranges: &RenderVisibilityRanges, render_mesh_instances: &RenderMeshInstancesGpu, @@ -1572,6 +1582,7 @@ fn extract_mesh_for_gpu_building( tag, not_shadow_caster, no_automatic_batching, + render_layers, ); let lightmap_uv_rect = pack_lightmap_uv_rect(lightmap.map(|lightmap| lightmap.uv_rect)); From 33b93fccb65af74b0f548c704f1cb95f1f114946 Mon Sep 17 00:00:00 2001 From: Adrian Graur Date: Tue, 6 May 2025 23:54:41 +0100 Subject: [PATCH 6/8] Fixed Warning. Signed-off-by: Adrian Graur --- crates/bevy_pbr/src/render/light.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_pbr/src/render/light.rs b/crates/bevy_pbr/src/render/light.rs index 302808666990d..83b6f59dc57a8 100644 --- a/crates/bevy_pbr/src/render/light.rs +++ b/crates/bevy_pbr/src/render/light.rs @@ -1993,7 +1993,7 @@ pub fn queue_shadows( // Skip the entity if it's not in the camera render layers. if let (Some(camera_layers), Some(layers)) = (camera_layers, mesh_layers) { - if !camera_layers.intersects(&layers) { + if !camera_layers.intersects(layers) { continue; } } From 133df02d69f4a551ec18f00583ab56220bf51c7b Mon Sep 17 00:00:00 2001 From: Adrian Graur Date: Fri, 9 May 2025 21:54:08 +0100 Subject: [PATCH 7/8] Changed layers retrieval to `unwrap_or_default()`. Signed-off-by: Adrian Graur --- crates/bevy_pbr/src/render/light.rs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/crates/bevy_pbr/src/render/light.rs b/crates/bevy_pbr/src/render/light.rs index 83b6f59dc57a8..cd93f9ebbebdc 100644 --- a/crates/bevy_pbr/src/render/light.rs +++ b/crates/bevy_pbr/src/render/light.rs @@ -1989,13 +1989,16 @@ pub fn queue_shadows( continue; } - let mesh_layers = mesh_instance.shared.render_layers.as_ref(); + let mesh_layers = mesh_instance + .shared + .render_layers + .as_ref() + .unwrap_or_default(); - // Skip the entity if it's not in the camera render layers. - if let (Some(camera_layers), Some(layers)) = (camera_layers, mesh_layers) { - if !camera_layers.intersects(layers) { - continue; - } + let camera_layers = camera_layers.unwrap_or_default(); + + if !camera_layers.intersects(&mesh_layers) { + continue; } // Skip the entity if it's cached in a bin and up to date. From cfc0099f8bcf0666887edc0327a01a2492392e3a Mon Sep 17 00:00:00 2001 From: Adrian Graur Date: Fri, 9 May 2025 22:23:29 +0100 Subject: [PATCH 8/8] Fixed reference warning. Signed-off-by: Adrian Graur --- crates/bevy_pbr/src/render/light.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_pbr/src/render/light.rs b/crates/bevy_pbr/src/render/light.rs index cd93f9ebbebdc..9b4568528008b 100644 --- a/crates/bevy_pbr/src/render/light.rs +++ b/crates/bevy_pbr/src/render/light.rs @@ -1997,7 +1997,7 @@ pub fn queue_shadows( let camera_layers = camera_layers.unwrap_or_default(); - if !camera_layers.intersects(&mesh_layers) { + if !camera_layers.intersects(mesh_layers) { continue; }