Skip to content

Commit

Permalink
light renderlayers
Browse files Browse the repository at this point in the history
  • Loading branch information
robtfm committed Dec 7, 2023
1 parent 182fb06 commit 1d7b868
Show file tree
Hide file tree
Showing 8 changed files with 86 additions and 24 deletions.
68 changes: 51 additions & 17 deletions crates/bevy_pbr/src/light.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1136,6 +1136,7 @@ pub(crate) struct PointLightAssignmentData {
range: f32,
shadows_enabled: bool,
spot_light_angle: Option<f32>,
render_layers: RenderLayers,
}

impl PointLightAssignmentData {
Expand Down Expand Up @@ -1176,10 +1177,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<Vec<PointLightAssignmentData>>,
mut cluster_aabb_spheres: Local<Vec<Option<Sphere>>>,
mut max_point_lights_warning_emitted: Local<bool>,
Expand All @@ -1198,12 +1212,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(),
}
},
),
);
Expand All @@ -1212,12 +1229,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(),
}
},
),
);
Expand Down Expand Up @@ -1247,7 +1267,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| {
Expand Down Expand Up @@ -1279,9 +1299,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) {
Expand Down Expand Up @@ -1503,6 +1532,11 @@ pub(crate) fn assign_lights_to_clusters(

let mut update_from_light_intersections = |visible_lights: &mut Vec<Entity>| {
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
Expand Down
8 changes: 7 additions & 1 deletion crates/bevy_pbr/src/render/light.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -48,6 +48,7 @@ pub struct ExtractedDirectionalLight {
shadow_normal_bias: f32,
cascade_shadow_config: CascadeShadowConfig,
cascades: HashMap<Entity, Vec<Cascade>>,
render_layers: RenderLayers,
}

#[derive(Copy, Clone, ShaderType, Default, Debug)]
Expand Down Expand Up @@ -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!
Expand Down Expand Up @@ -311,6 +313,7 @@ pub fn extract_lights(
&CascadeShadowConfig,
&GlobalTransform,
&ViewVisibility,
Option<&RenderLayers>,
),
Without<SpotLight>,
>,
Expand Down Expand Up @@ -426,6 +429,7 @@ pub fn extract_lights(
cascade_config,
transform,
view_visibility,
maybe_layers,
) in &directional_lights
{
if !view_visibility.get() {
Expand All @@ -445,6 +449,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,
));
Expand Down Expand Up @@ -879,6 +884,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;
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_pbr/src/render/mesh_view_types.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
7 changes: 7 additions & 0 deletions crates/bevy_pbr/src/render/pbr_functions.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
5 changes: 4 additions & 1 deletion crates/bevy_render/src/view/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ pub struct ViewUniform {
frustum: [Vec4; 6],
color_grading: ColorGrading,
mip_bias: f32,
render_layers: u32,
}

#[derive(Resource, Default)]
Expand Down Expand Up @@ -357,6 +358,7 @@ pub fn prepare_view_uniforms(
Option<&Frustum>,
Option<&TemporalJitter>,
Option<&MipBias>,
Option<&RenderLayers>,
)>,
) {
let view_iter = views.iter();
Expand All @@ -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;
Expand Down Expand Up @@ -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(),
}),
};

Expand Down
1 change: 1 addition & 0 deletions crates/bevy_render/src/view/view.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,5 @@ struct View {
frustum: array<vec4<f32>, 6>,
color_grading: ColorGrading,
mip_bias: f32,
render_layers: u32,
};
5 changes: 5 additions & 0 deletions crates/bevy_render/src/view/visibility/render_layers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down
15 changes: 10 additions & 5 deletions examples/3d/render_to_texture.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down

0 comments on commit 1d7b868

Please sign in to comment.