From 4eefe5fd91c85b2e6c4984108bdddbdadca78ed9 Mon Sep 17 00:00:00 2001 From: robtfm <50659922+robtfm@users.noreply.github.com> Date: Sun, 26 Nov 2023 01:10:08 +0000 Subject: [PATCH] light renderlayers --- crates/bevy_pbr/src/light.rs | 68 ++++++++++++++----- crates/bevy_pbr/src/render/light.rs | 8 ++- .../bevy_pbr/src/render/mesh_view_types.wgsl | 1 + crates/bevy_pbr/src/render/pbr_functions.wgsl | 7 ++ crates/bevy_render/src/view/mod.rs | 5 +- crates/bevy_render/src/view/view.wgsl | 1 + .../src/view/visibility/render_layers.rs | 5 ++ examples/3d/render_to_texture.rs | 15 ++-- 8 files changed, 86 insertions(+), 24 deletions(-) diff --git a/crates/bevy_pbr/src/light.rs b/crates/bevy_pbr/src/light.rs index 9902ce391c550..453454e0ec4ff 100644 --- a/crates/bevy_pbr/src/light.rs +++ b/crates/bevy_pbr/src/light.rs @@ -1140,6 +1140,7 @@ pub(crate) struct PointLightAssignmentData { range: f32, shadows_enabled: bool, spot_light_angle: Option, + render_layers: RenderLayers, } impl PointLightAssignmentData { @@ -1180,10 +1181,23 @@ pub(crate) fn assign_lights_to_clusters( &Frustum, &ClusterConfig, &mut Clusters, + Option<&RenderLayers>, Option<&mut VisiblePointLights>, )>, - point_lights_query: Query<(Entity, &GlobalTransform, &PointLight, &ViewVisibility)>, - spot_lights_query: Query<(Entity, &GlobalTransform, &SpotLight, &ViewVisibility)>, + point_lights_query: Query<( + Entity, + &GlobalTransform, + &PointLight, + Option<&RenderLayers>, + &ViewVisibility, + )>, + spot_lights_query: Query<( + Entity, + &GlobalTransform, + &SpotLight, + Option<&RenderLayers>, + &ViewVisibility, + )>, mut lights: Local>, mut cluster_aabb_spheres: Local>>, mut max_point_lights_warning_emitted: Local, @@ -1202,12 +1216,15 @@ pub(crate) fn assign_lights_to_clusters( .iter() .filter(|(.., visibility)| visibility.get()) .map( - |(entity, transform, point_light, _visibility)| PointLightAssignmentData { - entity, - transform: GlobalTransform::from_translation(transform.translation()), - shadows_enabled: point_light.shadows_enabled, - range: point_light.range, - spot_light_angle: None, + |(entity, transform, point_light, maybe_layers, _visibility)| { + PointLightAssignmentData { + entity, + transform: GlobalTransform::from_translation(transform.translation()), + shadows_enabled: point_light.shadows_enabled, + range: point_light.range, + spot_light_angle: None, + render_layers: maybe_layers.copied().unwrap_or_default(), + } }, ), ); @@ -1216,12 +1233,15 @@ pub(crate) fn assign_lights_to_clusters( .iter() .filter(|(.., visibility)| visibility.get()) .map( - |(entity, transform, spot_light, _visibility)| PointLightAssignmentData { - entity, - transform: *transform, - shadows_enabled: spot_light.shadows_enabled, - range: spot_light.range, - spot_light_angle: Some(spot_light.outer_angle), + |(entity, transform, spot_light, maybe_layers, _visibility)| { + PointLightAssignmentData { + entity, + transform: *transform, + shadows_enabled: spot_light.shadows_enabled, + range: spot_light.range, + spot_light_angle: Some(spot_light.outer_angle), + render_layers: maybe_layers.copied().unwrap_or_default(), + } }, ), ); @@ -1251,7 +1271,7 @@ pub(crate) fn assign_lights_to_clusters( // check each light against each view's frustum, keep only those that affect at least one of our views let frusta: Vec<_> = views .iter() - .map(|(_, _, _, frustum, _, _, _)| *frustum) + .map(|(_, _, _, frustum, _, _, _, _)| *frustum) .collect(); let mut lights_in_view_count = 0; lights.retain(|light| { @@ -1283,9 +1303,18 @@ pub(crate) fn assign_lights_to_clusters( lights.truncate(MAX_UNIFORM_BUFFER_POINT_LIGHTS); } - for (view_entity, camera_transform, camera, frustum, config, clusters, mut visible_lights) in - &mut views + for ( + view_entity, + camera_transform, + camera, + frustum, + config, + clusters, + maybe_layers, + mut visible_lights, + ) in &mut views { + let view_layers = maybe_layers.copied().unwrap_or_default(); let clusters = clusters.into_inner(); if matches!(config, ClusterConfig::None) { @@ -1507,6 +1536,11 @@ pub(crate) fn assign_lights_to_clusters( let mut update_from_light_intersections = |visible_lights: &mut Vec| { for light in &lights { + // check if the light layers overlap the view layers + if !view_layers.intersects(&light.render_layers) { + continue; + } + let light_sphere = light.sphere(); // Check if the light is within the view frustum diff --git a/crates/bevy_pbr/src/render/light.rs b/crates/bevy_pbr/src/render/light.rs index d7eff2ede461e..581ff0480e534 100644 --- a/crates/bevy_pbr/src/render/light.rs +++ b/crates/bevy_pbr/src/render/light.rs @@ -11,7 +11,7 @@ use bevy_render::{ render_resource::*, renderer::{RenderContext, RenderDevice, RenderQueue}, texture::*, - view::{ExtractedView, ViewVisibility, VisibleEntities}, + view::{ExtractedView, RenderLayers, ViewVisibility, VisibleEntities}, Extract, }; use bevy_transform::{components::GlobalTransform, prelude::Transform}; @@ -48,6 +48,7 @@ pub struct ExtractedDirectionalLight { shadow_normal_bias: f32, cascade_shadow_config: CascadeShadowConfig, cascades: HashMap>, + render_layers: RenderLayers, } #[derive(Copy, Clone, ShaderType, Default, Debug)] @@ -169,6 +170,7 @@ pub struct GpuDirectionalLight { num_cascades: u32, cascades_overlap_proportion: f32, depth_texture_base_index: u32, + render_layers: u32, } // NOTE: These must match the bit flags in bevy_pbr/src/render/mesh_view_types.wgsl! @@ -315,6 +317,7 @@ pub fn extract_lights( &CascadeShadowConfig, &GlobalTransform, &ViewVisibility, + Option<&RenderLayers>, ), Without, >, @@ -430,6 +433,7 @@ pub fn extract_lights( cascade_config, transform, view_visibility, + maybe_layers, ) in &directional_lights { if !view_visibility.get() { @@ -449,6 +453,7 @@ pub fn extract_lights( shadow_normal_bias: directional_light.shadow_normal_bias * std::f32::consts::SQRT_2, cascade_shadow_config: cascade_config.clone(), cascades: cascades.cascades.clone(), + render_layers: maybe_layers.copied().unwrap_or_default(), }, render_visible_entities, )); @@ -883,6 +888,7 @@ pub fn prepare_lights( num_cascades: num_cascades as u32, cascades_overlap_proportion: light.cascade_shadow_config.overlap_proportion, depth_texture_base_index: num_directional_cascades_enabled as u32, + render_layers: light.render_layers.bits(), }; if index < directional_shadow_enabled_count { num_directional_cascades_enabled += num_cascades; diff --git a/crates/bevy_pbr/src/render/mesh_view_types.wgsl b/crates/bevy_pbr/src/render/mesh_view_types.wgsl index f115d49d7898a..3062ad671a77f 100644 --- a/crates/bevy_pbr/src/render/mesh_view_types.wgsl +++ b/crates/bevy_pbr/src/render/mesh_view_types.wgsl @@ -33,6 +33,7 @@ struct DirectionalLight { num_cascades: u32, cascades_overlap_proportion: f32, depth_texture_base_index: u32, + render_layers: u32, }; const DIRECTIONAL_LIGHT_FLAGS_SHADOWS_ENABLED_BIT: u32 = 1u; diff --git a/crates/bevy_pbr/src/render/pbr_functions.wgsl b/crates/bevy_pbr/src/render/pbr_functions.wgsl index 9b4668f3e75ec..66d815db7ec4a 100644 --- a/crates/bevy_pbr/src/render/pbr_functions.wgsl +++ b/crates/bevy_pbr/src/render/pbr_functions.wgsl @@ -267,6 +267,13 @@ fn apply_pbr_lighting( // directional lights (direct) let n_directional_lights = view_bindings::lights.n_directional_lights; for (var i: u32 = 0u; i < n_directional_lights; i = i + 1u) { + // check the directional light render layers intersect the view render layers + // note this is not necessary for point and spot lights, as the relevant lights are filtered in `assign_lights_to_clusters` + let light = &view_bindings::lights.directional_lights[i]; + if ((*light).render_layers & view_bindings::view.render_layers) == 0u { + continue; + } + var shadow: f32 = 1.0; if ((in.flags & MESH_FLAGS_SHADOW_RECEIVER_BIT) != 0u && (view_bindings::lights.directional_lights[i].flags & mesh_view_types::DIRECTIONAL_LIGHT_FLAGS_SHADOWS_ENABLED_BIT) != 0u) { diff --git a/crates/bevy_render/src/view/mod.rs b/crates/bevy_render/src/view/mod.rs index 73d8bf9c24e80..7a0b928d7cced 100644 --- a/crates/bevy_render/src/view/mod.rs +++ b/crates/bevy_render/src/view/mod.rs @@ -172,6 +172,7 @@ pub struct ViewUniform { frustum: [Vec4; 6], color_grading: ColorGrading, mip_bias: f32, + render_layers: u32, } #[derive(Resource, Default)] @@ -357,6 +358,7 @@ pub fn prepare_view_uniforms( Option<&Frustum>, Option<&TemporalJitter>, Option<&MipBias>, + Option<&RenderLayers>, )>, ) { let view_iter = views.iter(); @@ -368,7 +370,7 @@ pub fn prepare_view_uniforms( else { return; }; - for (entity, camera, frustum, temporal_jitter, mip_bias) in &views { + for (entity, camera, frustum, temporal_jitter, mip_bias, maybe_layers) in &views { let viewport = camera.viewport.as_vec4(); let unjittered_projection = camera.projection; let mut projection = unjittered_projection; @@ -408,6 +410,7 @@ pub fn prepare_view_uniforms( frustum, color_grading: camera.color_grading, mip_bias: mip_bias.unwrap_or(&MipBias(0.0)).0, + render_layers: maybe_layers.copied().unwrap_or_default().bits(), }), }; diff --git a/crates/bevy_render/src/view/view.wgsl b/crates/bevy_render/src/view/view.wgsl index caa2b3a122f3e..a48fb19f56382 100644 --- a/crates/bevy_render/src/view/view.wgsl +++ b/crates/bevy_render/src/view/view.wgsl @@ -21,4 +21,5 @@ struct View { frustum: array, 6>, color_grading: ColorGrading, mip_bias: f32, + render_layers: u32, }; diff --git a/crates/bevy_render/src/view/visibility/render_layers.rs b/crates/bevy_render/src/view/visibility/render_layers.rs index 0451e35818f15..ecb3d79b6a778 100644 --- a/crates/bevy_render/src/view/visibility/render_layers.rs +++ b/crates/bevy_render/src/view/visibility/render_layers.rs @@ -110,6 +110,11 @@ impl RenderLayers { pub fn intersects(&self, other: &RenderLayers) -> bool { (self.0 & other.0) > 0 } + + /// get the bitmask representation of the contained layers + pub fn bits(&self) -> u32 { + self.0 + } } #[cfg(test)] diff --git a/examples/3d/render_to_texture.rs b/examples/3d/render_to_texture.rs index 81c687e5a7389..cd09df25b079a 100644 --- a/examples/3d/render_to_texture.rs +++ b/examples/3d/render_to_texture.rs @@ -88,11 +88,16 @@ fn setup( )); // Light - // NOTE: Currently lights are shared between passes - see https://github.com/bevyengine/bevy/issues/3462 - commands.spawn(PointLightBundle { - transform: Transform::from_translation(Vec3::new(0.0, 0.0, 10.0)), - ..default() - }); + // NOTE: we add the light to all layers so it affects both the rendered-to-texture cube, and the cube on which we display the texture + // Setting the layer to RenderLayers::layer(0) would cause the main view to be lit, but the rendered-to-texture cube to be unlit. + // Setting the layer to RenderLayers::layer(1) would cause the rendered-to-texture cube to be lit, but the main view to be unlit. + commands.spawn(( + PointLightBundle { + transform: Transform::from_translation(Vec3::new(0.0, 0.0, 10.0)), + ..default() + }, + RenderLayers::all(), + )); commands.spawn(( Camera3dBundle {