mirror of
https://github.com/bevyengine/bevy
synced 2024-11-25 06:00:20 +00:00
Per-light toggleable shadow mapping (#3126)
# Objective Allow shadow mapping to be enabled/disabled per-light. ## Solution - NOTE: This PR is on top of https://github.com/bevyengine/bevy/pull/3072 - Add `shadows_enabled` boolean property to `PointLight` and `DirectionalLight` components. - Do not update the frusta for the light if shadows are disabled. - Do not check for visible entities for the light if shadows are disabled. - Do not fetch shadows for lights with shadows disabled. - I reworked a few types for clarity: `ViewLight` -> `ShadowView`, the bulk of `ViewLights` members -> `ViewShadowBindings`, the entities Vec in `ViewLights` -> `ViewLightEntities`, the uniform offset in `ViewLights` for `GpuLights` -> `ViewLightsUniformOffset` Co-authored-by: Carter Anderson <mcanders1@gmail.com>
This commit is contained in:
parent
2e79951659
commit
a7729319cc
5 changed files with 204 additions and 103 deletions
|
@ -33,6 +33,7 @@ pub struct PointLight {
|
||||||
pub intensity: f32,
|
pub intensity: f32,
|
||||||
pub range: f32,
|
pub range: f32,
|
||||||
pub radius: f32,
|
pub radius: f32,
|
||||||
|
pub shadows_enabled: bool,
|
||||||
pub shadow_depth_bias: f32,
|
pub shadow_depth_bias: f32,
|
||||||
/// A bias applied along the direction of the fragment's surface normal. It is scaled to the
|
/// A bias applied along the direction of the fragment's surface normal. It is scaled to the
|
||||||
/// shadow map's texel size so that it can be small close to the camera and gets larger further
|
/// shadow map's texel size so that it can be small close to the camera and gets larger further
|
||||||
|
@ -48,6 +49,7 @@ impl Default for PointLight {
|
||||||
intensity: 800.0, // Roughly a 60W non-halogen incandescent bulb
|
intensity: 800.0, // Roughly a 60W non-halogen incandescent bulb
|
||||||
range: 20.0,
|
range: 20.0,
|
||||||
radius: 0.0,
|
radius: 0.0,
|
||||||
|
shadows_enabled: false,
|
||||||
shadow_depth_bias: Self::DEFAULT_SHADOW_DEPTH_BIAS,
|
shadow_depth_bias: Self::DEFAULT_SHADOW_DEPTH_BIAS,
|
||||||
shadow_normal_bias: Self::DEFAULT_SHADOW_NORMAL_BIAS,
|
shadow_normal_bias: Self::DEFAULT_SHADOW_NORMAL_BIAS,
|
||||||
}
|
}
|
||||||
|
@ -101,6 +103,7 @@ pub struct DirectionalLight {
|
||||||
pub color: Color,
|
pub color: Color,
|
||||||
/// Illuminance in lux
|
/// Illuminance in lux
|
||||||
pub illuminance: f32,
|
pub illuminance: f32,
|
||||||
|
pub shadows_enabled: bool,
|
||||||
pub shadow_projection: OrthographicProjection,
|
pub shadow_projection: OrthographicProjection,
|
||||||
pub shadow_depth_bias: f32,
|
pub shadow_depth_bias: f32,
|
||||||
/// A bias applied along the direction of the fragment's surface normal. It is scaled to the
|
/// A bias applied along the direction of the fragment's surface normal. It is scaled to the
|
||||||
|
@ -114,6 +117,7 @@ impl Default for DirectionalLight {
|
||||||
DirectionalLight {
|
DirectionalLight {
|
||||||
color: Color::rgb(1.0, 1.0, 1.0),
|
color: Color::rgb(1.0, 1.0, 1.0),
|
||||||
illuminance: 100000.0,
|
illuminance: 100000.0,
|
||||||
|
shadows_enabled: false,
|
||||||
shadow_projection: OrthographicProjection {
|
shadow_projection: OrthographicProjection {
|
||||||
left: -size,
|
left: -size,
|
||||||
right: size,
|
right: size,
|
||||||
|
@ -178,6 +182,13 @@ pub fn update_directional_light_frusta(
|
||||||
mut views: Query<(&GlobalTransform, &DirectionalLight, &mut Frustum)>,
|
mut views: Query<(&GlobalTransform, &DirectionalLight, &mut Frustum)>,
|
||||||
) {
|
) {
|
||||||
for (transform, directional_light, mut frustum) in views.iter_mut() {
|
for (transform, directional_light, mut frustum) in views.iter_mut() {
|
||||||
|
// The frustum is used for culling meshes to the light for shadow mapping
|
||||||
|
// so if shadow mapping is disabled for this light, then the frustum is
|
||||||
|
// not needed.
|
||||||
|
if !directional_light.shadows_enabled {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
let view_projection = directional_light.shadow_projection.get_projection_matrix()
|
let view_projection = directional_light.shadow_projection.get_projection_matrix()
|
||||||
* transform.compute_matrix().inverse();
|
* transform.compute_matrix().inverse();
|
||||||
*frustum = Frustum::from_view_projection(
|
*frustum = Frustum::from_view_projection(
|
||||||
|
@ -199,6 +210,13 @@ pub fn update_point_light_frusta(
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
for (transform, point_light, mut cubemap_frusta) in views.iter_mut() {
|
for (transform, point_light, mut cubemap_frusta) in views.iter_mut() {
|
||||||
|
// The frusta are used for culling meshes to the light for shadow mapping
|
||||||
|
// so if shadow mapping is disabled for this light, then the frusta are
|
||||||
|
// not needed.
|
||||||
|
if !point_light.shadows_enabled {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// ignore scale because we don't want to effectively scale light radius and range
|
// ignore scale because we don't want to effectively scale light radius and range
|
||||||
// by applying those as a view transform to shadow map rendering of objects
|
// by applying those as a view transform to shadow map rendering of objects
|
||||||
// and ignore rotation because we want the shadow map projections to align with the axes
|
// and ignore rotation because we want the shadow map projections to align with the axes
|
||||||
|
@ -227,10 +245,12 @@ pub fn check_light_visibility(
|
||||||
&mut CubemapVisibleEntities,
|
&mut CubemapVisibleEntities,
|
||||||
Option<&RenderLayers>,
|
Option<&RenderLayers>,
|
||||||
)>,
|
)>,
|
||||||
mut directional_lights: Query<
|
mut directional_lights: Query<(
|
||||||
(&Frustum, &mut VisibleEntities, Option<&RenderLayers>),
|
&DirectionalLight,
|
||||||
With<DirectionalLight>,
|
&Frustum,
|
||||||
>,
|
&mut VisibleEntities,
|
||||||
|
Option<&RenderLayers>,
|
||||||
|
)>,
|
||||||
mut visible_entity_query: Query<
|
mut visible_entity_query: Query<
|
||||||
(
|
(
|
||||||
Entity,
|
Entity,
|
||||||
|
@ -244,8 +264,16 @@ pub fn check_light_visibility(
|
||||||
>,
|
>,
|
||||||
) {
|
) {
|
||||||
// Directonal lights
|
// Directonal lights
|
||||||
for (frustum, mut visible_entities, maybe_view_mask) in directional_lights.iter_mut() {
|
for (directional_light, frustum, mut visible_entities, maybe_view_mask) in
|
||||||
|
directional_lights.iter_mut()
|
||||||
|
{
|
||||||
visible_entities.entities.clear();
|
visible_entities.entities.clear();
|
||||||
|
|
||||||
|
// NOTE: If shadow mapping is disabled for the light then it must have no visible entities
|
||||||
|
if !directional_light.shadows_enabled {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
let view_mask = maybe_view_mask.copied().unwrap_or_default();
|
let view_mask = maybe_view_mask.copied().unwrap_or_default();
|
||||||
|
|
||||||
for (
|
for (
|
||||||
|
@ -288,6 +316,12 @@ pub fn check_light_visibility(
|
||||||
for visible_entities in cubemap_visible_entities.iter_mut() {
|
for visible_entities in cubemap_visible_entities.iter_mut() {
|
||||||
visible_entities.entities.clear();
|
visible_entities.entities.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NOTE: If shadow mapping is disabled for the light then it must have no visible entities
|
||||||
|
if !point_light.shadows_enabled {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
let view_mask = maybe_view_mask.copied().unwrap_or_default();
|
let view_mask = maybe_view_mask.copied().unwrap_or_default();
|
||||||
let light_sphere = Sphere {
|
let light_sphere = Sphere {
|
||||||
center: transform.translation,
|
center: transform.translation,
|
||||||
|
|
|
@ -50,6 +50,7 @@ pub struct ExtractedPointLight {
|
||||||
range: f32,
|
range: f32,
|
||||||
radius: f32,
|
radius: f32,
|
||||||
transform: GlobalTransform,
|
transform: GlobalTransform,
|
||||||
|
shadows_enabled: bool,
|
||||||
shadow_depth_bias: f32,
|
shadow_depth_bias: f32,
|
||||||
shadow_normal_bias: f32,
|
shadow_normal_bias: f32,
|
||||||
}
|
}
|
||||||
|
@ -61,6 +62,7 @@ pub struct ExtractedDirectionalLight {
|
||||||
illuminance: f32,
|
illuminance: f32,
|
||||||
direction: Vec3,
|
direction: Vec3,
|
||||||
projection: Mat4,
|
projection: Mat4,
|
||||||
|
shadows_enabled: bool,
|
||||||
shadow_depth_bias: f32,
|
shadow_depth_bias: f32,
|
||||||
shadow_normal_bias: f32,
|
shadow_normal_bias: f32,
|
||||||
}
|
}
|
||||||
|
@ -77,20 +79,42 @@ pub struct GpuPointLight {
|
||||||
radius: f32,
|
radius: f32,
|
||||||
near: f32,
|
near: f32,
|
||||||
far: f32,
|
far: f32,
|
||||||
|
flags: u32,
|
||||||
shadow_depth_bias: f32,
|
shadow_depth_bias: f32,
|
||||||
shadow_normal_bias: f32,
|
shadow_normal_bias: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NOTE: These must match the bit flags in bevy_pbr2/src/render/pbr.frag!
|
||||||
|
bitflags::bitflags! {
|
||||||
|
#[repr(transparent)]
|
||||||
|
struct PointLightFlags: u32 {
|
||||||
|
const SHADOWS_ENABLED = (1 << 0);
|
||||||
|
const NONE = 0;
|
||||||
|
const UNINITIALIZED = 0xFFFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Copy, Clone, AsStd140, Default, Debug)]
|
#[derive(Copy, Clone, AsStd140, Default, Debug)]
|
||||||
pub struct GpuDirectionalLight {
|
pub struct GpuDirectionalLight {
|
||||||
view_projection: Mat4,
|
view_projection: Mat4,
|
||||||
color: Vec4,
|
color: Vec4,
|
||||||
dir_to_light: Vec3,
|
dir_to_light: Vec3,
|
||||||
|
flags: u32,
|
||||||
shadow_depth_bias: f32,
|
shadow_depth_bias: f32,
|
||||||
shadow_normal_bias: f32,
|
shadow_normal_bias: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NOTE: These must match the bit flags in bevy_pbr2/src/render/pbr.frag!
|
||||||
|
bitflags::bitflags! {
|
||||||
|
#[repr(transparent)]
|
||||||
|
struct DirectionalLightFlags: u32 {
|
||||||
|
const SHADOWS_ENABLED = (1 << 0);
|
||||||
|
const NONE = 0;
|
||||||
|
const UNINITIALIZED = 0xFFFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Copy, Clone, Debug, AsStd140)]
|
#[derive(Copy, Clone, Debug, AsStd140)]
|
||||||
pub struct GpuLights {
|
pub struct GpuLights {
|
||||||
|
@ -328,6 +352,7 @@ pub fn extract_lights(
|
||||||
range: point_light.range,
|
range: point_light.range,
|
||||||
radius: point_light.radius,
|
radius: point_light.radius,
|
||||||
transform: *transform,
|
transform: *transform,
|
||||||
|
shadows_enabled: point_light.shadows_enabled,
|
||||||
shadow_depth_bias: point_light.shadow_depth_bias,
|
shadow_depth_bias: point_light.shadow_depth_bias,
|
||||||
// The factor of SQRT_2 is for the worst-case diagonal offset
|
// The factor of SQRT_2 is for the worst-case diagonal offset
|
||||||
shadow_normal_bias: point_light.shadow_normal_bias
|
shadow_normal_bias: point_light.shadow_normal_bias
|
||||||
|
@ -357,6 +382,7 @@ pub fn extract_lights(
|
||||||
illuminance: directional_light.illuminance,
|
illuminance: directional_light.illuminance,
|
||||||
direction: transform.forward(),
|
direction: transform.forward(),
|
||||||
projection: directional_light.shadow_projection.get_projection_matrix(),
|
projection: directional_light.shadow_projection.get_projection_matrix(),
|
||||||
|
shadows_enabled: directional_light.shadows_enabled,
|
||||||
shadow_depth_bias: directional_light.shadow_depth_bias,
|
shadow_depth_bias: directional_light.shadow_depth_bias,
|
||||||
// The factor of SQRT_2 is for the worst-case diagonal offset
|
// The factor of SQRT_2 is for the worst-case diagonal offset
|
||||||
shadow_normal_bias: directional_light.shadow_normal_bias
|
shadow_normal_bias: directional_light.shadow_normal_bias
|
||||||
|
@ -424,18 +450,24 @@ fn face_index_to_name(face_index: usize) -> &'static str {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ViewLight {
|
pub struct ShadowView {
|
||||||
pub depth_texture_view: TextureView,
|
pub depth_texture_view: TextureView,
|
||||||
pub pass_name: String,
|
pub pass_name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ViewLights {
|
pub struct ViewShadowBindings {
|
||||||
pub point_light_depth_texture: Texture,
|
pub point_light_depth_texture: Texture,
|
||||||
pub point_light_depth_texture_view: TextureView,
|
pub point_light_depth_texture_view: TextureView,
|
||||||
pub directional_light_depth_texture: Texture,
|
pub directional_light_depth_texture: Texture,
|
||||||
pub directional_light_depth_texture_view: TextureView,
|
pub directional_light_depth_texture_view: TextureView,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ViewLightEntities {
|
||||||
pub lights: Vec<Entity>,
|
pub lights: Vec<Entity>,
|
||||||
pub gpu_light_binding_index: u32,
|
}
|
||||||
|
|
||||||
|
pub struct ViewLightsUniformOffset {
|
||||||
|
pub offset: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
@ -524,54 +556,59 @@ pub fn prepare_lights(
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: this should select lights based on relevance to the view instead of the first ones that show up in a query
|
// TODO: this should select lights based on relevance to the view instead of the first ones that show up in a query
|
||||||
for (light_index, (light_entity, light)) in
|
for (light_index, (light_entity, light)) in point_lights.iter().enumerate() {
|
||||||
point_lights.iter().enumerate().take(MAX_POINT_LIGHTS)
|
|
||||||
{
|
|
||||||
// ignore scale because we don't want to effectively scale light radius and range
|
// ignore scale because we don't want to effectively scale light radius and range
|
||||||
// by applying those as a view transform to shadow map rendering of objects
|
// by applying those as a view transform to shadow map rendering of objects
|
||||||
// and ignore rotation because we want the shadow map projections to align with the axes
|
// and ignore rotation because we want the shadow map projections to align with the axes
|
||||||
let view_translation = GlobalTransform::from_translation(light.transform.translation);
|
let view_translation = GlobalTransform::from_translation(light.transform.translation);
|
||||||
|
|
||||||
for (face_index, view_rotation) in cube_face_rotations.iter().enumerate() {
|
if light.shadows_enabled {
|
||||||
let depth_texture_view =
|
for (face_index, view_rotation) in cube_face_rotations.iter().enumerate() {
|
||||||
point_light_depth_texture
|
let depth_texture_view =
|
||||||
.texture
|
point_light_depth_texture
|
||||||
.create_view(&TextureViewDescriptor {
|
.texture
|
||||||
label: Some("point_light_shadow_map_texture_view"),
|
.create_view(&TextureViewDescriptor {
|
||||||
format: None,
|
label: Some("point_light_shadow_map_texture_view"),
|
||||||
dimension: Some(TextureViewDimension::D2),
|
format: None,
|
||||||
aspect: TextureAspect::All,
|
dimension: Some(TextureViewDimension::D2),
|
||||||
base_mip_level: 0,
|
aspect: TextureAspect::All,
|
||||||
mip_level_count: None,
|
base_mip_level: 0,
|
||||||
base_array_layer: (light_index * 6 + face_index) as u32,
|
mip_level_count: None,
|
||||||
array_layer_count: NonZeroU32::new(1),
|
base_array_layer: (light_index * 6 + face_index) as u32,
|
||||||
});
|
array_layer_count: NonZeroU32::new(1),
|
||||||
|
});
|
||||||
|
|
||||||
let view_light_entity = commands
|
let view_light_entity = commands
|
||||||
.spawn()
|
.spawn()
|
||||||
.insert_bundle((
|
.insert_bundle((
|
||||||
ViewLight {
|
ShadowView {
|
||||||
depth_texture_view,
|
depth_texture_view,
|
||||||
pass_name: format!(
|
pass_name: format!(
|
||||||
"shadow pass point light {} {}",
|
"shadow pass point light {} {}",
|
||||||
light_index,
|
light_index,
|
||||||
face_index_to_name(face_index)
|
face_index_to_name(face_index)
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
ExtractedView {
|
ExtractedView {
|
||||||
width: point_light_shadow_map.size as u32,
|
width: point_light_shadow_map.size as u32,
|
||||||
height: point_light_shadow_map.size as u32,
|
height: point_light_shadow_map.size as u32,
|
||||||
transform: view_translation * *view_rotation,
|
transform: view_translation * *view_rotation,
|
||||||
projection: cube_face_projection,
|
projection: cube_face_projection,
|
||||||
},
|
},
|
||||||
RenderPhase::<Shadow>::default(),
|
RenderPhase::<Shadow>::default(),
|
||||||
LightEntity::Point {
|
LightEntity::Point {
|
||||||
light_entity,
|
light_entity,
|
||||||
face_index,
|
face_index,
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
.id();
|
.id();
|
||||||
view_lights.push(view_light_entity);
|
view_lights.push(view_light_entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut flags = PointLightFlags::NONE;
|
||||||
|
if light.shadows_enabled {
|
||||||
|
flags |= PointLightFlags::SHADOWS_ENABLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
gpu_lights.point_lights[light_index] = GpuPointLight {
|
gpu_lights.point_lights[light_index] = GpuPointLight {
|
||||||
|
@ -584,6 +621,7 @@ pub fn prepare_lights(
|
||||||
inverse_square_range: 1.0 / (light.range * light.range),
|
inverse_square_range: 1.0 / (light.range * light.range),
|
||||||
near: 0.1,
|
near: 0.1,
|
||||||
far: light.range,
|
far: light.range,
|
||||||
|
flags: flags.bits,
|
||||||
shadow_depth_bias: light.shadow_depth_bias,
|
shadow_depth_bias: light.shadow_depth_bias,
|
||||||
shadow_normal_bias: light.shadow_normal_bias,
|
shadow_normal_bias: light.shadow_normal_bias,
|
||||||
};
|
};
|
||||||
|
@ -616,6 +654,11 @@ pub fn prepare_lights(
|
||||||
// NOTE: This orthographic projection defines the volume within which shadows from a directional light can be cast
|
// NOTE: This orthographic projection defines the volume within which shadows from a directional light can be cast
|
||||||
let projection = light.projection;
|
let projection = light.projection;
|
||||||
|
|
||||||
|
let mut flags = DirectionalLightFlags::NONE;
|
||||||
|
if light.shadows_enabled {
|
||||||
|
flags |= DirectionalLightFlags::SHADOWS_ENABLED;
|
||||||
|
}
|
||||||
|
|
||||||
gpu_lights.directional_lights[i] = GpuDirectionalLight {
|
gpu_lights.directional_lights[i] = GpuDirectionalLight {
|
||||||
// premultiply color by intensity
|
// premultiply color by intensity
|
||||||
// we don't use the alpha at all, so no reason to multiply only [0..3]
|
// we don't use the alpha at all, so no reason to multiply only [0..3]
|
||||||
|
@ -623,42 +666,45 @@ pub fn prepare_lights(
|
||||||
dir_to_light,
|
dir_to_light,
|
||||||
// NOTE: * view is correct, it should not be view.inverse() here
|
// NOTE: * view is correct, it should not be view.inverse() here
|
||||||
view_projection: projection * view,
|
view_projection: projection * view,
|
||||||
|
flags: flags.bits,
|
||||||
shadow_depth_bias: light.shadow_depth_bias,
|
shadow_depth_bias: light.shadow_depth_bias,
|
||||||
shadow_normal_bias: light.shadow_normal_bias,
|
shadow_normal_bias: light.shadow_normal_bias,
|
||||||
};
|
};
|
||||||
|
|
||||||
let depth_texture_view =
|
if light.shadows_enabled {
|
||||||
directional_light_depth_texture
|
let depth_texture_view =
|
||||||
.texture
|
directional_light_depth_texture
|
||||||
.create_view(&TextureViewDescriptor {
|
.texture
|
||||||
label: Some("directional_light_shadow_map_texture_view"),
|
.create_view(&TextureViewDescriptor {
|
||||||
format: None,
|
label: Some("directional_light_shadow_map_texture_view"),
|
||||||
dimension: Some(TextureViewDimension::D2),
|
format: None,
|
||||||
aspect: TextureAspect::All,
|
dimension: Some(TextureViewDimension::D2),
|
||||||
base_mip_level: 0,
|
aspect: TextureAspect::All,
|
||||||
mip_level_count: None,
|
base_mip_level: 0,
|
||||||
base_array_layer: i as u32,
|
mip_level_count: None,
|
||||||
array_layer_count: NonZeroU32::new(1),
|
base_array_layer: i as u32,
|
||||||
});
|
array_layer_count: NonZeroU32::new(1),
|
||||||
|
});
|
||||||
|
|
||||||
let view_light_entity = commands
|
let view_light_entity = commands
|
||||||
.spawn()
|
.spawn()
|
||||||
.insert_bundle((
|
.insert_bundle((
|
||||||
ViewLight {
|
ShadowView {
|
||||||
depth_texture_view,
|
depth_texture_view,
|
||||||
pass_name: format!("shadow pass directional light {}", i),
|
pass_name: format!("shadow pass directional light {}", i),
|
||||||
},
|
},
|
||||||
ExtractedView {
|
ExtractedView {
|
||||||
width: directional_light_shadow_map.size as u32,
|
width: directional_light_shadow_map.size as u32,
|
||||||
height: directional_light_shadow_map.size as u32,
|
height: directional_light_shadow_map.size as u32,
|
||||||
transform: GlobalTransform::from_matrix(view.inverse()),
|
transform: GlobalTransform::from_matrix(view.inverse()),
|
||||||
projection,
|
projection,
|
||||||
},
|
},
|
||||||
RenderPhase::<Shadow>::default(),
|
RenderPhase::<Shadow>::default(),
|
||||||
LightEntity::Directional { light_entity },
|
LightEntity::Directional { light_entity },
|
||||||
))
|
))
|
||||||
.id();
|
.id();
|
||||||
view_lights.push(view_light_entity);
|
view_lights.push(view_light_entity);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
let point_light_depth_texture_view =
|
let point_light_depth_texture_view =
|
||||||
point_light_depth_texture
|
point_light_depth_texture
|
||||||
|
@ -686,14 +732,20 @@ pub fn prepare_lights(
|
||||||
array_layer_count: None,
|
array_layer_count: None,
|
||||||
});
|
});
|
||||||
|
|
||||||
commands.entity(entity).insert(ViewLights {
|
commands.entity(entity).insert_bundle((
|
||||||
point_light_depth_texture: point_light_depth_texture.texture,
|
ViewShadowBindings {
|
||||||
point_light_depth_texture_view,
|
point_light_depth_texture: point_light_depth_texture.texture,
|
||||||
directional_light_depth_texture: directional_light_depth_texture.texture,
|
point_light_depth_texture_view,
|
||||||
directional_light_depth_texture_view,
|
directional_light_depth_texture: directional_light_depth_texture.texture,
|
||||||
lights: view_lights,
|
directional_light_depth_texture_view,
|
||||||
gpu_light_binding_index: light_meta.view_gpu_lights.push(gpu_lights),
|
},
|
||||||
});
|
ViewLightEntities {
|
||||||
|
lights: view_lights,
|
||||||
|
},
|
||||||
|
ViewLightsUniformOffset {
|
||||||
|
offset: light_meta.view_gpu_lights.push(gpu_lights),
|
||||||
|
},
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
light_meta
|
light_meta
|
||||||
|
@ -728,12 +780,12 @@ pub fn queue_shadows(
|
||||||
render_meshes: Res<RenderAssets<Mesh>>,
|
render_meshes: Res<RenderAssets<Mesh>>,
|
||||||
mut pipelines: ResMut<SpecializedPipelines<ShadowPipeline>>,
|
mut pipelines: ResMut<SpecializedPipelines<ShadowPipeline>>,
|
||||||
mut pipeline_cache: ResMut<RenderPipelineCache>,
|
mut pipeline_cache: ResMut<RenderPipelineCache>,
|
||||||
mut view_lights: Query<&ViewLights>,
|
view_lights: Query<&ViewLightEntities>,
|
||||||
mut view_light_shadow_phases: Query<(&LightEntity, &mut RenderPhase<Shadow>)>,
|
mut view_light_shadow_phases: Query<(&LightEntity, &mut RenderPhase<Shadow>)>,
|
||||||
point_light_entities: Query<&CubemapVisibleEntities, With<ExtractedPointLight>>,
|
point_light_entities: Query<&CubemapVisibleEntities, With<ExtractedPointLight>>,
|
||||||
directional_light_entities: Query<&VisibleEntities, With<ExtractedDirectionalLight>>,
|
directional_light_entities: Query<&VisibleEntities, With<ExtractedDirectionalLight>>,
|
||||||
) {
|
) {
|
||||||
for view_lights in view_lights.iter_mut() {
|
for view_lights in view_lights.iter() {
|
||||||
let draw_shadow_mesh = shadow_draw_functions
|
let draw_shadow_mesh = shadow_draw_functions
|
||||||
.read()
|
.read()
|
||||||
.get_id::<DrawShadowMesh>()
|
.get_id::<DrawShadowMesh>()
|
||||||
|
@ -753,6 +805,8 @@ pub fn queue_shadows(
|
||||||
.expect("Failed to get point light visible entities")
|
.expect("Failed to get point light visible entities")
|
||||||
.get(*face_index),
|
.get(*face_index),
|
||||||
};
|
};
|
||||||
|
// NOTE: Lights with shadow mapping disabled will have no visible entities
|
||||||
|
// so no meshes will be queued
|
||||||
for VisibleEntity { entity, .. } in visible_entities.iter() {
|
for VisibleEntity { entity, .. } in visible_entities.iter() {
|
||||||
let mut key = ShadowPipelineKey::empty();
|
let mut key = ShadowPipelineKey::empty();
|
||||||
if let Ok(mesh_handle) = casting_meshes.get(*entity) {
|
if let Ok(mesh_handle) = casting_meshes.get(*entity) {
|
||||||
|
@ -811,8 +865,8 @@ impl CachedPipelinePhaseItem for Shadow {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ShadowPassNode {
|
pub struct ShadowPassNode {
|
||||||
main_view_query: QueryState<&'static ViewLights>,
|
main_view_query: QueryState<&'static ViewLightEntities>,
|
||||||
view_light_query: QueryState<(&'static ViewLight, &'static RenderPhase<Shadow>)>,
|
view_light_query: QueryState<(&'static ShadowView, &'static RenderPhase<Shadow>)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ShadowPassNode {
|
impl ShadowPassNode {
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
use crate::{LightMeta, NotShadowCaster, NotShadowReceiver, ShadowPipeline, ViewLights};
|
use crate::{
|
||||||
|
LightMeta, NotShadowCaster, NotShadowReceiver, ShadowPipeline, ViewLightsUniformOffset,
|
||||||
|
ViewShadowBindings,
|
||||||
|
};
|
||||||
use bevy_app::Plugin;
|
use bevy_app::Plugin;
|
||||||
use bevy_asset::{Assets, Handle, HandleUntyped};
|
use bevy_asset::{Assets, Handle, HandleUntyped};
|
||||||
use bevy_ecs::{
|
use bevy_ecs::{
|
||||||
|
@ -523,13 +526,13 @@ pub fn queue_mesh_view_bind_groups(
|
||||||
shadow_pipeline: Res<ShadowPipeline>,
|
shadow_pipeline: Res<ShadowPipeline>,
|
||||||
light_meta: Res<LightMeta>,
|
light_meta: Res<LightMeta>,
|
||||||
view_uniforms: Res<ViewUniforms>,
|
view_uniforms: Res<ViewUniforms>,
|
||||||
mut views: Query<(Entity, &ViewLights)>,
|
mut views: Query<(Entity, &ViewShadowBindings)>,
|
||||||
) {
|
) {
|
||||||
if let (Some(view_binding), Some(light_binding)) = (
|
if let (Some(view_binding), Some(light_binding)) = (
|
||||||
view_uniforms.uniforms.binding(),
|
view_uniforms.uniforms.binding(),
|
||||||
light_meta.view_gpu_lights.binding(),
|
light_meta.view_gpu_lights.binding(),
|
||||||
) {
|
) {
|
||||||
for (entity, view_lights) in views.iter_mut() {
|
for (entity, view_shadow_bindings) in views.iter_mut() {
|
||||||
let view_bind_group = render_device.create_bind_group(&BindGroupDescriptor {
|
let view_bind_group = render_device.create_bind_group(&BindGroupDescriptor {
|
||||||
entries: &[
|
entries: &[
|
||||||
BindGroupEntry {
|
BindGroupEntry {
|
||||||
|
@ -543,7 +546,7 @@ pub fn queue_mesh_view_bind_groups(
|
||||||
BindGroupEntry {
|
BindGroupEntry {
|
||||||
binding: 2,
|
binding: 2,
|
||||||
resource: BindingResource::TextureView(
|
resource: BindingResource::TextureView(
|
||||||
&view_lights.point_light_depth_texture_view,
|
&view_shadow_bindings.point_light_depth_texture_view,
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
BindGroupEntry {
|
BindGroupEntry {
|
||||||
|
@ -553,7 +556,7 @@ pub fn queue_mesh_view_bind_groups(
|
||||||
BindGroupEntry {
|
BindGroupEntry {
|
||||||
binding: 4,
|
binding: 4,
|
||||||
resource: BindingResource::TextureView(
|
resource: BindingResource::TextureView(
|
||||||
&view_lights.directional_light_depth_texture_view,
|
&view_shadow_bindings.directional_light_depth_texture_view,
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
BindGroupEntry {
|
BindGroupEntry {
|
||||||
|
@ -578,7 +581,7 @@ pub struct SetMeshViewBindGroup<const I: usize>;
|
||||||
impl<const I: usize> EntityRenderCommand for SetMeshViewBindGroup<I> {
|
impl<const I: usize> EntityRenderCommand for SetMeshViewBindGroup<I> {
|
||||||
type Param = SQuery<(
|
type Param = SQuery<(
|
||||||
Read<ViewUniformOffset>,
|
Read<ViewUniformOffset>,
|
||||||
Read<ViewLights>,
|
Read<ViewLightsUniformOffset>,
|
||||||
Read<MeshViewBindGroup>,
|
Read<MeshViewBindGroup>,
|
||||||
)>;
|
)>;
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -592,7 +595,7 @@ impl<const I: usize> EntityRenderCommand for SetMeshViewBindGroup<I> {
|
||||||
pass.set_bind_group(
|
pass.set_bind_group(
|
||||||
I,
|
I,
|
||||||
&mesh_view_bind_group.value,
|
&mesh_view_bind_group.value,
|
||||||
&[view_uniform.offset, view_lights.gpu_light_binding_index],
|
&[view_uniform.offset, view_lights.offset],
|
||||||
);
|
);
|
||||||
|
|
||||||
RenderCommandResult::Success
|
RenderCommandResult::Success
|
||||||
|
|
|
@ -13,18 +13,26 @@ struct PointLight {
|
||||||
radius: f32;
|
radius: f32;
|
||||||
near: f32;
|
near: f32;
|
||||||
far: f32;
|
far: f32;
|
||||||
|
// 'flags' is a bit field indicating various options. u32 is 32 bits so we have up to 32 options.
|
||||||
|
flags: u32;
|
||||||
shadow_depth_bias: f32;
|
shadow_depth_bias: f32;
|
||||||
shadow_normal_bias: f32;
|
shadow_normal_bias: f32;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let POINT_LIGHT_FLAGS_SHADOWS_ENABLED_BIT: u32 = 1u;
|
||||||
|
|
||||||
struct DirectionalLight {
|
struct DirectionalLight {
|
||||||
view_projection: mat4x4<f32>;
|
view_projection: mat4x4<f32>;
|
||||||
color: vec4<f32>;
|
color: vec4<f32>;
|
||||||
direction_to_light: vec3<f32>;
|
direction_to_light: vec3<f32>;
|
||||||
|
// 'flags' is a bit field indicating various options. u32 is 32 bits so we have up to 32 options.
|
||||||
|
flags: u32;
|
||||||
shadow_depth_bias: f32;
|
shadow_depth_bias: f32;
|
||||||
shadow_normal_bias: f32;
|
shadow_normal_bias: f32;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let DIRECTIONAL_LIGHT_FLAGS_SHADOWS_ENABLED_BIT: u32 = 1u;
|
||||||
|
|
||||||
[[block]]
|
[[block]]
|
||||||
struct Lights {
|
struct Lights {
|
||||||
// NOTE: this array size must be kept in sync with the constants defined bevy_pbr2/src/render/light.rs
|
// NOTE: this array size must be kept in sync with the constants defined bevy_pbr2/src/render/light.rs
|
||||||
|
@ -47,4 +55,4 @@ var point_shadow_textures_sampler: sampler_comparison;
|
||||||
[[group(0), binding(4)]]
|
[[group(0), binding(4)]]
|
||||||
var directional_shadow_textures: texture_depth_2d_array;
|
var directional_shadow_textures: texture_depth_2d_array;
|
||||||
[[group(0), binding(5)]]
|
[[group(0), binding(5)]]
|
||||||
var directional_shadow_textures_sampler: sampler_comparison;
|
var directional_shadow_textures_sampler: sampler_comparison;
|
||||||
|
|
|
@ -500,7 +500,8 @@ fn fragment(in: FragmentInput) -> [[location(0)]] vec4<f32> {
|
||||||
for (var i: i32 = 0; i < n_point_lights; i = i + 1) {
|
for (var i: i32 = 0; i < n_point_lights; i = i + 1) {
|
||||||
let light = lights.point_lights[i];
|
let light = lights.point_lights[i];
|
||||||
var shadow: f32;
|
var shadow: f32;
|
||||||
if ((mesh.flags & MESH_FLAGS_SHADOW_RECEIVER_BIT) != 0u) {
|
if ((mesh.flags & MESH_FLAGS_SHADOW_RECEIVER_BIT) != 0u
|
||||||
|
|| (light.flags & POINT_LIGHT_FLAGS_SHADOWS_ENABLED_BIT) != 0u) {
|
||||||
shadow = fetch_point_shadow(i, in.world_position, in.world_normal);
|
shadow = fetch_point_shadow(i, in.world_position, in.world_normal);
|
||||||
} else {
|
} else {
|
||||||
shadow = 1.0;
|
shadow = 1.0;
|
||||||
|
@ -511,7 +512,8 @@ fn fragment(in: FragmentInput) -> [[location(0)]] vec4<f32> {
|
||||||
for (var i: i32 = 0; i < n_directional_lights; i = i + 1) {
|
for (var i: i32 = 0; i < n_directional_lights; i = i + 1) {
|
||||||
let light = lights.directional_lights[i];
|
let light = lights.directional_lights[i];
|
||||||
var shadow: f32;
|
var shadow: f32;
|
||||||
if ((mesh.flags & MESH_FLAGS_SHADOW_RECEIVER_BIT) != 0u) {
|
if ((mesh.flags & MESH_FLAGS_SHADOW_RECEIVER_BIT) != 0u
|
||||||
|
|| (light.flags & DIRECTIONAL_LIGHT_FLAGS_SHADOWS_ENABLED_BIT) != 0u) {
|
||||||
shadow = fetch_directional_shadow(i, in.world_position, in.world_normal);
|
shadow = fetch_directional_shadow(i, in.world_position, in.world_normal);
|
||||||
} else {
|
} else {
|
||||||
shadow = 1.0;
|
shadow = 1.0;
|
||||||
|
|
Loading…
Reference in a new issue