Skip to content
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
165 changes: 105 additions & 60 deletions crates/bevy_pbr/src/render/light.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,6 @@ pub struct GpuDirectionalLight {
num_cascades: u32,
cascades_overlap_proportion: f32,
depth_texture_base_index: u32,
skip: u32,
}

// NOTE: These must match the bit flags in bevy_pbr/src/render/mesh_view_types.wgsl!
Expand Down Expand Up @@ -982,57 +981,37 @@ pub fn prepare_lights(
global_light_meta.entity_to_index.insert(entity, index);
}

let mut gpu_directional_lights = [GpuDirectionalLight::default(); MAX_DIRECTIONAL_LIGHTS];
// iterate the views once to find the maximum number of cascade shadowmaps we will need
let mut num_directional_cascades_enabled = 0usize;
for (index, (_light_entity, _, light)) in directional_lights
for (
_entity,
_camera_main_entity,
_extracted_view,
_clusters,
maybe_layers,
_no_indirect_drawing,
_maybe_ambient_override,
) in sorted_cameras
.0
.iter()
.enumerate()
.take(MAX_DIRECTIONAL_LIGHTS)
.filter_map(|sorted_camera| views.get(sorted_camera.entity).ok())
{
let mut flags = DirectionalLightFlags::NONE;

// Lights are sorted, volumetric and shadow enabled lights are first
if light.volumetric
&& light.shadows_enabled
&& (index < directional_volumetric_enabled_count)
{
flags |= DirectionalLightFlags::VOLUMETRIC;
}
// Shadow enabled lights are second
if light.shadows_enabled && (index < directional_shadow_enabled_count) {
flags |= DirectionalLightFlags::SHADOWS_ENABLED;
}

if light.affects_lightmapped_mesh_diffuse {
flags |= DirectionalLightFlags::AFFECTS_LIGHTMAPPED_MESH_DIFFUSE;
let mut num_directional_cascades_for_this_view = 0usize;
let render_layers = maybe_layers.unwrap_or_default();

for (_light_entity, _, light) in directional_lights.iter() {
if light.shadows_enabled && light.render_layers.intersects(render_layers) {
num_directional_cascades_for_this_view += light
.cascade_shadow_config
.bounds
.len()
.min(MAX_CASCADES_PER_LIGHT);
}
}

let num_cascades = light
.cascade_shadow_config
.bounds
.len()
.min(MAX_CASCADES_PER_LIGHT);
gpu_directional_lights[index] = GpuDirectionalLight {
// Set to true later when necessary.
skip: 0u32,
// Filled in later.
cascades: [GpuDirectionalCascade::default(); MAX_CASCADES_PER_LIGHT],
// premultiply color by illuminance
// we don't use the alpha at all, so no reason to multiply only [0..3]
color: Vec4::from_slice(&light.color.to_f32_array()) * light.illuminance,
// direction is negated to be ready for N.L
dir_to_light: light.transform.back().into(),
flags: flags.bits(),
soft_shadow_size: light.soft_shadow_size.unwrap_or_default(),
shadow_depth_bias: light.shadow_depth_bias,
shadow_normal_bias: light.shadow_normal_bias,
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,
};
if index < directional_shadow_enabled_count {
num_directional_cascades_enabled += num_cascades;
}
num_directional_cascades_enabled = num_directional_cascades_enabled
.max(num_directional_cascades_for_this_view)
.min(max_texture_array_layers);
}

global_light_meta
Expand Down Expand Up @@ -1157,6 +1136,7 @@ pub fn prepare_lights(
{
live_views.insert(entity);

let view_layers = maybe_layers.unwrap_or_default();
let mut view_lights = Vec::new();
let mut view_occlusion_culling_lights = Vec::new();

Expand All @@ -1176,6 +1156,68 @@ pub fn prepare_lights(

let n_clusters = clusters.dimensions.x * clusters.dimensions.y * clusters.dimensions.z;
let ambient_light = maybe_ambient_override.unwrap_or(&ambient_light);

let mut gpu_directional_lights = [GpuDirectionalLight::default(); MAX_DIRECTIONAL_LIGHTS];
let mut num_directional_cascades_enabled_for_this_view = 0usize;
let mut num_directional_lights_for_this_view = 0usize;
for (index, (_light_entity, _, light)) in directional_lights
.iter()
.filter(|(_light_entity, _, light)| light.render_layers.intersects(view_layers))
.enumerate()
.take(MAX_DIRECTIONAL_LIGHTS)
{
num_directional_lights_for_this_view += 1;

let mut flags = DirectionalLightFlags::NONE;

// Lights are sorted, volumetric and shadow enabled lights are first
if light.volumetric
&& light.shadows_enabled
&& (index < directional_volumetric_enabled_count)
{
flags |= DirectionalLightFlags::VOLUMETRIC;
}

// Shadow enabled lights are second
let mut num_cascades = 0;
if light.shadows_enabled {
let cascades = light
.cascade_shadow_config
.bounds
.len()
.min(MAX_CASCADES_PER_LIGHT);

if num_directional_cascades_enabled_for_this_view + cascades
<= max_texture_array_layers
{
flags |= DirectionalLightFlags::SHADOWS_ENABLED;
num_cascades += cascades;
}
}

if light.affects_lightmapped_mesh_diffuse {
flags |= DirectionalLightFlags::AFFECTS_LIGHTMAPPED_MESH_DIFFUSE;
}

gpu_directional_lights[index] = GpuDirectionalLight {
// Filled in later.
cascades: [GpuDirectionalCascade::default(); MAX_CASCADES_PER_LIGHT],
// premultiply color by illuminance
// we don't use the alpha at all, so no reason to multiply only [0..3]
color: Vec4::from_slice(&light.color.to_f32_array()) * light.illuminance,
// direction is negated to be ready for N.L
dir_to_light: light.transform.back().into(),
flags: flags.bits(),
soft_shadow_size: light.soft_shadow_size.unwrap_or_default(),
shadow_depth_bias: light.shadow_depth_bias,
shadow_normal_bias: light.shadow_normal_bias,
num_cascades: num_cascades as u32,
cascades_overlap_proportion: light.cascade_shadow_config.overlap_proportion,
depth_texture_base_index: num_directional_cascades_enabled_for_this_view as u32,
};
num_directional_cascades_enabled_for_this_view += num_cascades;
}

let mut gpu_lights = GpuLights {
directional_lights: gpu_directional_lights,
ambient_color: Vec4::from_slice(&LinearRgba::from(ambient_light.color).to_f32_array())
Expand All @@ -1187,8 +1229,7 @@ pub fn prepare_lights(
cluster_factors_zw.y,
),
cluster_dimensions: clusters.dimensions.extend(n_clusters),
n_directional_lights: directional_lights.iter().len().min(MAX_DIRECTIONAL_LIGHTS)
as u32,
n_directional_lights: num_directional_lights_for_this_view as u32,
// spotlight shadow maps are stored in the directional light array, starting at num_directional_cascades_enabled.
// the spot lights themselves start in the light array at point_light_count. so to go from light
// index to shadow map index, we need to subtract point light count and add directional shadowmap count.
Expand Down Expand Up @@ -1418,27 +1459,31 @@ pub fn prepare_lights(
}

// directional lights
// clear entities for lights that don't intersect the layer
for &(light_entity, _, _) in directional_lights
.iter()
.filter(|(_, _, light)| !light.render_layers.intersects(view_layers))
{
let Ok(mut light_view_entities) = light_view_entities.get_mut(light_entity) else {
continue;
};
if let Some(entities) = light_view_entities.remove(&entity) {
despawn_entities(&mut commands, entities);
}
}

let mut directional_depth_texture_array_index = 0u32;
let view_layers = maybe_layers.unwrap_or_default();
for (light_index, &(light_entity, light_main_entity, light)) in directional_lights
.iter()
.filter(|(_, _, light)| light.render_layers.intersects(view_layers))
.enumerate()
.take(MAX_DIRECTIONAL_LIGHTS)
{
let gpu_light = &mut gpu_lights.directional_lights[light_index];

let Ok(mut light_view_entities) = light_view_entities.get_mut(light_entity) else {
continue;
};

// Check if the light intersects with the view.
if !view_layers.intersects(&light.render_layers) {
gpu_light.skip = 1u32;
if let Some(entities) = light_view_entities.remove(&entity) {
despawn_entities(&mut commands, entities);
}
continue;
}
let gpu_light = &mut gpu_lights.directional_lights[light_index];

// Only deal with cascades when shadows are enabled.
if (gpu_light.flags & DirectionalLightFlags::SHADOWS_ENABLED.bits()) == 0u32 {
Expand Down
1 change: 0 additions & 1 deletion crates/bevy_pbr/src/render/mesh_view_types.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ struct DirectionalLight {
num_cascades: u32,
cascades_overlap_proportion: f32,
depth_texture_base_index: u32,
skip: u32,
};

const DIRECTIONAL_LIGHT_FLAGS_SHADOWS_ENABLED_BIT: u32 = 1u;
Expand Down
3 changes: 0 additions & 3 deletions crates/bevy_pbr/src/render/pbr_functions.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -511,9 +511,6 @@ fn apply_pbr_lighting(
// check if this light should be skipped, which occurs if this light does not intersect with the view
// note point and spot lights aren't skippable, as the relevant lights are filtered in `assign_lights_to_clusters`
let light = &view_bindings::lights.directional_lights[i];
if (*light).skip != 0u {
continue;
}

// If we're lightmapped, disable diffuse contribution from the light if
// requested, to avoid double-counting light.
Expand Down