Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

light renderlayers #10742

Merged
merged 1 commit into from
Dec 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 51 additions & 17 deletions crates/bevy_pbr/src/light.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1140,6 +1140,7 @@ pub(crate) struct PointLightAssignmentData {
range: f32,
shadows_enabled: bool,
spot_light_angle: Option<f32>,
render_layers: RenderLayers,
}

impl PointLightAssignmentData {
Expand Down Expand Up @@ -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<Vec<PointLightAssignmentData>>,
mut cluster_aabb_spheres: Local<Vec<Option<Sphere>>>,
mut max_point_lights_warning_emitted: Local<bool>,
Expand All @@ -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(),
}
},
),
);
Expand All @@ -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(),
}
},
),
);
Expand Down Expand Up @@ -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| {
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -1507,6 +1536,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 @@ -315,6 +317,7 @@ pub fn extract_lights(
&CascadeShadowConfig,
&GlobalTransform,
&ViewVisibility,
Option<&RenderLayers>,
),
Without<SpotLight>,
>,
Expand Down Expand Up @@ -430,6 +433,7 @@ pub fn extract_lights(
cascade_config,
transform,
view_visibility,
maybe_layers,
) in &directional_lights
{
if !view_visibility.get() {
Expand All @@ -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,
));
Expand Down Expand Up @@ -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;
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