Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_pbr/src/contact_shadows.rs
9395 views
1
//! Contact shadows implemented via screenspace raymarching.
2
3
use bevy_app::{App, Plugin};
4
use bevy_derive::{Deref, DerefMut};
5
use 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
};
14
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
15
use bevy_render::{
16
extract_component::{ExtractComponent, ExtractComponentPlugin},
17
render_resource::{DynamicUniformBuffer, ShaderType},
18
renderer::{RenderDevice, RenderQueue},
19
sync_component::SyncComponent,
20
view::ExtractedView,
21
Render, RenderApp, RenderSystems,
22
};
23
use bevy_utils::default;
24
25
/// Enables contact shadows for a camera.
26
pub struct ContactShadowsPlugin;
27
28
/// Add this component to a camera to enable contact shadows.
29
///
30
/// Contact shadows are a screen-space technique that adds small-scale shadows
31
/// in areas where traditional shadow maps may lack detail, such as where
32
/// objects touch the ground.
33
///
34
/// This can be used in forward or deferred rendering, but the depth prepass is required.
35
#[derive(Clone, Copy, Component, Reflect)]
36
#[reflect(Component, Default, Clone)]
37
#[require(bevy_core_pipeline::prepass::DepthPrepass)]
38
pub struct ContactShadows {
39
/// The number of steps to be taken at regular intervals to find an initial
40
/// intersection.
41
pub linear_steps: u32,
42
/// When marching the depth buffer, we only have 2.5D information and don't
43
/// know how thick surfaces are. We shall assume that the depth buffer
44
/// fragments are cuboids with a constant thickness defined by this
45
/// parameter.
46
pub thickness: f32,
47
/// The length of the contact shadow ray in world space.
48
pub length: f32,
49
}
50
51
impl Default for ContactShadows {
52
fn default() -> Self {
53
Self {
54
linear_steps: 16,
55
thickness: 0.1,
56
length: 0.3,
57
}
58
}
59
}
60
61
/// A version of [`ContactShadows`] for upload to the GPU.
62
#[derive(Clone, Copy, Component, ShaderType, Default)]
63
pub struct ContactShadowsUniform {
64
pub linear_steps: u32,
65
pub thickness: f32,
66
pub length: f32,
67
#[cfg(feature = "webgl")]
68
pub _padding: f32,
69
}
70
71
impl From<ContactShadows> for ContactShadowsUniform {
72
fn from(settings: ContactShadows) -> Self {
73
Self {
74
linear_steps: settings.linear_steps,
75
thickness: settings.thickness,
76
length: settings.length,
77
#[cfg(feature = "webgl")]
78
_padding: 0.0,
79
}
80
}
81
}
82
83
impl SyncComponent for ContactShadows {
84
type Out = Self;
85
}
86
87
impl ExtractComponent for ContactShadows {
88
type QueryData = &'static ContactShadows;
89
type QueryFilter = ();
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)]
98
pub struct ContactShadowsBuffer(pub DynamicUniformBuffer<ContactShadowsUniform>);
99
100
impl 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_resource::<ContactShadowsBuffer>()
111
.add_systems(
112
Render,
113
prepare_contact_shadows_settings.in_set(RenderSystems::PrepareResources),
114
);
115
}
116
}
117
118
fn prepare_contact_shadows_settings(
119
mut commands: Commands,
120
views: Query<(Entity, Option<&ContactShadows>), With<ExtractedView>>,
121
mut contact_shadows_buffer: ResMut<ContactShadowsBuffer>,
122
render_device: Res<RenderDevice>,
123
render_queue: Res<RenderQueue>,
124
) {
125
contact_shadows_buffer.0.clear();
126
for (entity, settings) in &views {
127
let uniform = if let Some(settings) = settings {
128
ContactShadowsUniform::from(*settings)
129
} else {
130
ContactShadowsUniform {
131
linear_steps: 0,
132
..default()
133
}
134
};
135
let offset = contact_shadows_buffer.0.push(&uniform);
136
commands
137
.entity(entity)
138
.insert(ViewContactShadowsUniformOffset(offset));
139
}
140
contact_shadows_buffer
141
.0
142
.write_buffer(&render_device, &render_queue);
143
}
144
145
/// A component that stores the offset within the [`ContactShadowsBuffer`] for
146
/// each view.
147
#[derive(Component, Default, Deref, DerefMut)]
148
pub struct ViewContactShadowsUniformOffset(pub u32);
149
150