Skip to main content

bevy_pbr/
contact_shadows.rs

1//! Contact shadows implemented via screenspace raymarching.
2
3use bevy_app::{App, Plugin};
4use bevy_derive::{Deref, DerefMut};
5use bevy_ecs::{
6    component::Component,
7    entity::Entity,
8    query::{QueryItem, With},
9    reflect::ReflectComponent,
10    resource::Resource,
11    schedule::IntoScheduleConfigs,
12    system::{Commands, Query, Res, ResMut},
13};
14use bevy_reflect::{std_traits::ReflectDefault, Reflect};
15use bevy_render::{
16    extract_component::{ExtractComponent, ExtractComponentPlugin},
17    render_resource::{DynamicUniformBuffer, ShaderType},
18    renderer::{RenderDevice, RenderQueue},
19    sync_component::SyncComponent,
20    view::ExtractedView,
21    GpuResourceAppExt, Render, RenderApp, RenderSystems,
22};
23
24/// Enables contact shadows for a camera.
25pub struct ContactShadowsPlugin;
26
27/// Add this component to a camera to enable contact shadows.
28///
29/// Contact shadows are a screen-space technique that adds small-scale shadows
30/// in areas where traditional shadow maps may lack detail, such as where
31/// objects touch the ground.
32///
33/// This can be used in forward or deferred rendering, but the depth prepass is required.
34#[derive(Clone, Copy, Component, Reflect)]
35#[reflect(Component, Default, Clone)]
36#[require(bevy_core_pipeline::prepass::DepthPrepass)]
37pub struct ContactShadows {
38    /// The number of steps to be taken at regular intervals to find an initial
39    /// intersection.
40    pub linear_steps: u32,
41    /// When marching the depth buffer, we only have 2.5D information and don't
42    /// know how thick surfaces are. We shall assume that the depth buffer
43    /// fragments are cuboids with a constant thickness defined by this
44    /// parameter.
45    pub thickness: f32,
46    /// The length of the contact shadow ray in world space.
47    pub length: f32,
48}
49
50impl Default for ContactShadows {
51    fn default() -> Self {
52        Self {
53            linear_steps: 16,
54            thickness: 0.1,
55            length: 0.3,
56        }
57    }
58}
59
60/// A version of [`ContactShadows`] for upload to the GPU.
61#[derive(Clone, Copy, ShaderType, Default)]
62pub struct ContactShadowsUniform {
63    pub linear_steps: u32,
64    pub thickness: f32,
65    pub length: f32,
66    #[cfg(feature = "webgl")]
67    pub _padding: f32,
68}
69
70impl From<ContactShadows> for ContactShadowsUniform {
71    fn from(settings: ContactShadows) -> Self {
72        Self {
73            linear_steps: settings.linear_steps,
74            thickness: settings.thickness,
75            length: settings.length,
76            #[cfg(feature = "webgl")]
77            _padding: 0.0,
78        }
79    }
80}
81
82impl SyncComponent for ContactShadows {
83    type Target = (Self, ViewContactShadowsUniformOffset);
84}
85
86impl ExtractComponent for ContactShadows {
87    type QueryData = &'static ContactShadows;
88    type QueryFilter = ();
89    type Out = Self;
90
91    fn extract_component(settings: QueryItem<'_, '_, Self::QueryData>) -> Option<Self::Out> {
92        Some(*settings)
93    }
94}
95
96/// A GPU buffer that stores the contact shadow settings for each view.
97#[derive(Resource, Default)]
98pub struct ContactShadowsBuffer(pub DynamicUniformBuffer<ContactShadowsUniform>);
99
100impl Plugin for ContactShadowsPlugin {
101    fn build(&self, app: &mut App) {
102        app.register_type::<ContactShadows>()
103            .add_plugins(ExtractComponentPlugin::<ContactShadows>::default());
104
105        let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
106            return;
107        };
108
109        render_app
110            .init_gpu_resource::<ContactShadowsBuffer>()
111            .add_systems(
112                Render,
113                prepare_contact_shadows_settings.in_set(RenderSystems::PrepareResources),
114            );
115    }
116}
117
118fn prepare_contact_shadows_settings(
119    mut commands: Commands,
120    views: Query<(Entity, &ContactShadows), With<ExtractedView>>,
121    mut contact_shadows_buffer: ResMut<ContactShadowsBuffer>,
122    render_device: Res<RenderDevice>,
123    render_queue: Res<RenderQueue>,
124) {
125    let Some(mut writer) =
126        contact_shadows_buffer
127            .0
128            .get_writer(views.iter().len(), &render_device, &render_queue)
129    else {
130        return;
131    };
132    for (entity, settings) in &views {
133        let uniform = ContactShadowsUniform::from(*settings);
134        let offset = writer.write(&uniform);
135        commands
136            .entity(entity)
137            .insert(ViewContactShadowsUniformOffset(offset));
138    }
139}
140
141/// A component that stores the offset within the [`ContactShadowsBuffer`] for
142/// each view.
143#[derive(Component, Default, Deref, DerefMut)]
144pub struct ViewContactShadowsUniformOffset(pub u32);