Skip to content

Commit ac87f8e

Browse files
committed
Add marker component for point and spot light shadow primary camera
1 parent f1fc851 commit ac87f8e

4 files changed

Lines changed: 52 additions & 14 deletions

File tree

crates/bevy_pbr/src/render/gpu_preprocess.rs

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,8 @@ use bevy_render::{
6262
renderer::{RenderContext, RenderDevice, RenderQueue, ViewQuery},
6363
settings::WgpuFeatures,
6464
view::{
65-
ExtractedView, NoIndirectDrawing, RenderVisibilityRanges, RetainedViewEntity, ViewUniform,
66-
ViewUniformOffset, ViewUniforms,
65+
ExtractedView, NoIndirectDrawing, PointAndSpotLightShadowPrimaryCamera,
66+
RenderVisibilityRanges, RetainedViewEntity, ViewUniform, ViewUniformOffset, ViewUniforms,
6767
},
6868
GpuResourceAppExt, Render, RenderApp, RenderSystems,
6969
};
@@ -617,7 +617,13 @@ pub fn early_gpu_preprocess(
617617
),
618618
Without<SkipGpuPreprocess>,
619619
>,
620-
camera_views: Query<&ExtractedView, (With<ExtractedCamera>, Without<SkipGpuPreprocess>)>,
620+
point_and_spot_light_shadow_primary_camera: Query<
621+
&ExtractedView,
622+
(
623+
With<ExtractedCamera>,
624+
With<PointAndSpotLightShadowPrimaryCamera>,
625+
),
626+
>,
621627
view_query: Query<
622628
(
623629
&ExtractedView,
@@ -650,6 +656,8 @@ pub fn early_gpu_preprocess(
650656
let (shadow_cascade_views, extracted_view, has_non_root_view) = current_view.into_inner();
651657
let all_views =
652658
gather_shadow_cascades_for_view(view_entity, shadow_cascade_views, &light_query);
659+
let point_and_spot_light_shadow_camera_view =
660+
point_and_spot_light_shadow_primary_camera.single();
653661

654662
// Run the compute passes.
655663
for view_entity in all_views {
@@ -668,16 +676,13 @@ pub fn early_gpu_preprocess(
668676
extracted_view.world_from_view.translation().to_array()
669677
} else {
670678
// PointLight and SpotLight shadow views are handled in this else block.
671-
// TODO: We need to better handle this case.
672-
// As written, point and spot lights shadow views will just use the first user camera
673-
// that is returned by the query.
674-
// If there is only one user camera, this is fine, but for multiple user cameras,
675-
// only one of them is randomly used for visibility range culling.
676-
let camera_view: Option<&ExtractedView> = camera_views.iter().next();
677-
if let Some(camera_view) = camera_view {
679+
// We could handle this case better if we can specify certain point and spot light shadow
680+
// views to different cameras. Right now, it is all centralized to one user specified camera.
681+
if let Ok(camera_view) = point_and_spot_light_shadow_camera_view {
678682
camera_view.world_from_view.translation().to_array()
679683
} else {
680-
// No camera views to render to.
684+
// There is no single designated primary camera to use for vis range culling of the shadows.
685+
// Do not render them.
681686
continue;
682687
}
683688
};

crates/bevy_render/src/camera.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@ use crate::{
1111
texture::{GpuImage, ManualTextureViews},
1212
view::{
1313
ColorGrading, ExtractedView, ExtractedWindows, Msaa, NoIndirectDrawing,
14-
RenderExtractedVisibleEntities, RenderVisibleEntities, RenderVisibleEntitiesClass,
15-
RetainedViewEntity, ViewUniformOffset, VisibilityExtractionSystemParam,
14+
PointAndSpotLightShadowPrimaryCamera, RenderExtractedVisibleEntities,
15+
RenderVisibleEntities, RenderVisibleEntitiesClass, RetainedViewEntity, ViewUniformOffset,
16+
VisibilityExtractionSystemParam,
1617
},
1718
Extract, ExtractSchedule, Render, RenderApp, RenderSystems,
1819
};
@@ -492,6 +493,7 @@ pub fn extract_cameras(
492493
Option<&MipBias>,
493494
Option<&RenderLayers>,
494495
Option<&Projection>,
496+
Has<PointAndSpotLightShadowPrimaryCamera>,
495497
Has<NoIndirectDrawing>,
496498
),
497499
)>,
@@ -517,6 +519,7 @@ pub fn extract_cameras(
517519
MipBias,
518520
RenderLayers,
519521
Projection,
522+
PointAndSpotLightShadowPrimaryCamera,
520523
NoIndirectDrawing,
521524
ViewUniformOffset,
522525
);
@@ -538,6 +541,7 @@ pub fn extract_cameras(
538541
mip_bias,
539542
render_layers,
540543
projection,
544+
has_pasl_shadow_primary_camera,
541545
no_indirect_drawing,
542546
),
543547
) in query.iter()
@@ -682,6 +686,12 @@ pub fn extract_cameras(
682686
commands.remove::<Projection>();
683687
}
684688

689+
if has_pasl_shadow_primary_camera {
690+
commands.insert(PointAndSpotLightShadowPrimaryCamera);
691+
} else {
692+
commands.remove::<PointAndSpotLightShadowPrimaryCamera>();
693+
}
694+
685695
if no_indirect_drawing
686696
|| !matches!(
687697
gpu_preprocessing_support.max_supported_mode,

crates/bevy_render/src/view/visibility/range.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
use super::VisibilityRange;
55
use bevy_app::{App, Plugin};
66
use bevy_ecs::{
7+
component::Component,
78
entity::Entity,
89
lifecycle::RemovedComponents,
910
query::Changed,
@@ -57,6 +58,23 @@ impl Plugin for RenderVisibilityRangePlugin {
5758
}
5859
}
5960

61+
/// A marker component for a `Camera` to denote that this entity should be the primary camera
62+
/// used for GPU Visibility Range Culling on shadows produced by all `PointLight`s and
63+
/// `SpotLight`s. There should only be one `Camera` with this marker component
64+
/// at a given moment in the application. This culling will occur if `GpuPreprocessingMode`
65+
/// is at least `GpuPreprocessingMode::PreprocessingOnly`.
66+
///
67+
/// Unlike `DirectionalLight` shadows, `PointLight` shadows and `SpotLight` shadows are
68+
/// not associated with any camera. They are only rendered once, regardless of the
69+
/// number of cameras. In order for these shadows to participate in visibility range culling,
70+
/// the user camera that distances are calculated relative to must be specified. Use this component
71+
/// to specify that camera.
72+
///
73+
/// If this marker component is not placed on a camera, or if there are multiple cameras
74+
/// with this component, shadows made by `PointLight`s and `SpotLight`s may not render.
75+
#[derive(Component, Default)]
76+
pub struct PointAndSpotLightShadowPrimaryCamera;
77+
6078
/// Stores information related to [`VisibilityRange`]s in the render world.
6179
#[derive(Resource)]
6280
pub struct RenderVisibilityRanges {

examples/testbed/3d.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ mod light {
122122
use bevy::{
123123
color::palettes::css::{DEEP_PINK, LIME, RED},
124124
prelude::*,
125+
render::view::PointAndSpotLightShadowPrimaryCamera,
125126
};
126127

127128
const CURRENT_SCENE: super::Scene = super::Scene::Light;
@@ -203,6 +204,7 @@ mod light {
203204

204205
commands.spawn((
205206
Camera3d::default(),
207+
PointAndSpotLightShadowPrimaryCamera,
206208
Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
207209
DespawnOnExit(CURRENT_SCENE),
208210
));
@@ -732,6 +734,7 @@ mod render_layers {
732734
use bevy::{
733735
camera::{visibility::RenderLayers, Viewport},
734736
prelude::*,
737+
render::view::PointAndSpotLightShadowPrimaryCamera,
735738
window::PrimaryWindow,
736739
};
737740

@@ -805,7 +808,9 @@ mod render_layers {
805808
DespawnOnExit(CURRENT_SCENE),
806809
));
807810
match index {
808-
0 => {}
811+
0 => {
812+
entity_cmds.insert(PointAndSpotLightShadowPrimaryCamera);
813+
}
809814
1 => {
810815
entity_cmds.insert(RenderLayers::layer(1));
811816
}

0 commit comments

Comments
 (0)