Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/examples/3d/shadow_biases.rs
6592 views
1
//! Demonstrates how shadow biases affect shadows in a 3d scene.
2
3
#[path = "../helpers/camera_controller.rs"]
4
mod camera_controller;
5
6
use bevy::{light::ShadowFilteringMethod, prelude::*};
7
use camera_controller::{CameraController, CameraControllerPlugin};
8
9
fn main() {
10
App::new()
11
.add_plugins(DefaultPlugins)
12
.add_plugins(CameraControllerPlugin)
13
.add_systems(Startup, setup)
14
.add_systems(
15
Update,
16
(
17
cycle_filter_methods,
18
adjust_light_position,
19
adjust_point_light_biases,
20
toggle_light,
21
adjust_directional_light_biases,
22
),
23
)
24
.run();
25
}
26
27
#[derive(Component)]
28
struct Lights;
29
30
/// set up a 3D scene to test shadow biases and perspective projections
31
fn setup(
32
mut commands: Commands,
33
mut meshes: ResMut<Assets<Mesh>>,
34
mut materials: ResMut<Assets<StandardMaterial>>,
35
) {
36
let spawn_plane_depth = 300.0f32;
37
let spawn_height = 2.0;
38
let sphere_radius = 0.25;
39
40
let white_handle = materials.add(StandardMaterial {
41
base_color: Color::WHITE,
42
perceptual_roughness: 1.0,
43
..default()
44
});
45
let sphere_handle = meshes.add(Sphere::new(sphere_radius));
46
47
let light_transform = Transform::from_xyz(5.0, 5.0, 0.0).looking_at(Vec3::ZERO, Vec3::Y);
48
commands.spawn((
49
light_transform,
50
Visibility::default(),
51
Lights,
52
children![
53
(PointLight {
54
intensity: 0.0,
55
range: spawn_plane_depth,
56
color: Color::WHITE,
57
shadows_enabled: true,
58
..default()
59
}),
60
(DirectionalLight {
61
shadows_enabled: true,
62
..default()
63
})
64
],
65
));
66
67
// camera
68
commands.spawn((
69
Camera3d::default(),
70
Transform::from_xyz(-1.0, 1.0, 1.0).looking_at(Vec3::new(-1.0, 1.0, 0.0), Vec3::Y),
71
CameraController::default(),
72
ShadowFilteringMethod::Hardware2x2,
73
));
74
75
for z_i32 in (-spawn_plane_depth as i32..=0).step_by(2) {
76
commands.spawn((
77
Mesh3d(sphere_handle.clone()),
78
MeshMaterial3d(white_handle.clone()),
79
Transform::from_xyz(
80
0.0,
81
if z_i32 % 4 == 0 {
82
spawn_height
83
} else {
84
sphere_radius
85
},
86
z_i32 as f32,
87
),
88
));
89
}
90
91
// ground plane
92
let plane_size = 2.0 * spawn_plane_depth;
93
commands.spawn((
94
Mesh3d(meshes.add(Plane3d::default().mesh().size(plane_size, plane_size))),
95
MeshMaterial3d(white_handle),
96
));
97
98
commands.spawn((
99
Node {
100
position_type: PositionType::Absolute,
101
padding: UiRect::all(px(5)),
102
..default()
103
},
104
BackgroundColor(Color::BLACK.with_alpha(0.75)),
105
GlobalZIndex(i32::MAX),
106
children![(
107
Text::default(),
108
children![
109
(TextSpan::new("Controls:\n")),
110
(TextSpan::new("R / Z - reset biases to default / zero\n")),
111
(TextSpan::new("L - switch between directional and point lights [",)),
112
(TextSpan::new("DirectionalLight")),
113
(TextSpan::new("]\n")),
114
(TextSpan::new("F - switch directional light filter methods [",)),
115
(TextSpan::new("Hardware2x2")),
116
(TextSpan::new("]\n")),
117
(TextSpan::new("1/2 - change point light depth bias [")),
118
(TextSpan::new("0.00")),
119
(TextSpan::new("]\n")),
120
(TextSpan::new("3/4 - change point light normal bias [")),
121
(TextSpan::new("0.0")),
122
(TextSpan::new("]\n")),
123
(TextSpan::new("5/6 - change direction light depth bias [")),
124
(TextSpan::new("0.00")),
125
(TextSpan::new("]\n")),
126
(TextSpan::new("7/8 - change direction light normal bias [",)),
127
(TextSpan::new("0.0")),
128
(TextSpan::new("]\n")),
129
(TextSpan::new(
130
"left/right/up/down/pgup/pgdown - adjust light position (looking at 0,0,0) [",
131
)),
132
(TextSpan(format!("{:.1},", light_transform.translation.x))),
133
(TextSpan(format!(" {:.1},", light_transform.translation.y))),
134
(TextSpan(format!(" {:.1}", light_transform.translation.z))),
135
(TextSpan::new("]\n")),
136
]
137
)],
138
));
139
}
140
141
fn toggle_light(
142
input: Res<ButtonInput<KeyCode>>,
143
mut point_lights: Query<&mut PointLight>,
144
mut directional_lights: Query<&mut DirectionalLight>,
145
example_text: Single<Entity, With<Text>>,
146
mut writer: TextUiWriter,
147
) {
148
if input.just_pressed(KeyCode::KeyL) {
149
for mut light in &mut point_lights {
150
light.intensity = if light.intensity == 0.0 {
151
*writer.text(*example_text, 4) = "PointLight".to_string();
152
light_consts::lumens::VERY_LARGE_CINEMA_LIGHT
153
} else {
154
0.0
155
};
156
}
157
for mut light in &mut directional_lights {
158
light.illuminance = if light.illuminance == 0.0 {
159
*writer.text(*example_text, 4) = "DirectionalLight".to_string();
160
light_consts::lux::AMBIENT_DAYLIGHT
161
} else {
162
0.0
163
};
164
}
165
}
166
}
167
168
fn adjust_light_position(
169
input: Res<ButtonInput<KeyCode>>,
170
mut lights: Query<&mut Transform, With<Lights>>,
171
example_text: Single<Entity, With<Text>>,
172
mut writer: TextUiWriter,
173
) {
174
let mut offset = Vec3::ZERO;
175
if input.just_pressed(KeyCode::ArrowLeft) {
176
offset.x -= 1.0;
177
}
178
if input.just_pressed(KeyCode::ArrowRight) {
179
offset.x += 1.0;
180
}
181
if input.just_pressed(KeyCode::ArrowUp) {
182
offset.z -= 1.0;
183
}
184
if input.just_pressed(KeyCode::ArrowDown) {
185
offset.z += 1.0;
186
}
187
if input.just_pressed(KeyCode::PageDown) {
188
offset.y -= 1.0;
189
}
190
if input.just_pressed(KeyCode::PageUp) {
191
offset.y += 1.0;
192
}
193
if offset != Vec3::ZERO {
194
let example_text = *example_text;
195
for mut light in &mut lights {
196
light.translation += offset;
197
light.look_at(Vec3::ZERO, Vec3::Y);
198
*writer.text(example_text, 22) = format!("{:.1},", light.translation.x);
199
*writer.text(example_text, 23) = format!(" {:.1},", light.translation.y);
200
*writer.text(example_text, 24) = format!(" {:.1}", light.translation.z);
201
}
202
}
203
}
204
205
fn cycle_filter_methods(
206
input: Res<ButtonInput<KeyCode>>,
207
mut filter_methods: Query<&mut ShadowFilteringMethod>,
208
example_text: Single<Entity, With<Text>>,
209
mut writer: TextUiWriter,
210
) {
211
if input.just_pressed(KeyCode::KeyF) {
212
for mut filter_method in &mut filter_methods {
213
let filter_method_string;
214
*filter_method = match *filter_method {
215
ShadowFilteringMethod::Hardware2x2 => {
216
filter_method_string = "Gaussian".to_string();
217
ShadowFilteringMethod::Gaussian
218
}
219
ShadowFilteringMethod::Gaussian => {
220
filter_method_string = "Temporal".to_string();
221
ShadowFilteringMethod::Temporal
222
}
223
ShadowFilteringMethod::Temporal => {
224
filter_method_string = "Hardware2x2".to_string();
225
ShadowFilteringMethod::Hardware2x2
226
}
227
};
228
*writer.text(*example_text, 7) = filter_method_string;
229
}
230
}
231
}
232
233
fn adjust_point_light_biases(
234
input: Res<ButtonInput<KeyCode>>,
235
mut query: Query<&mut PointLight>,
236
example_text: Single<Entity, With<Text>>,
237
mut writer: TextUiWriter,
238
) {
239
let depth_bias_step_size = 0.01;
240
let normal_bias_step_size = 0.1;
241
for mut light in &mut query {
242
if input.just_pressed(KeyCode::Digit1) {
243
light.shadow_depth_bias -= depth_bias_step_size;
244
}
245
if input.just_pressed(KeyCode::Digit2) {
246
light.shadow_depth_bias += depth_bias_step_size;
247
}
248
if input.just_pressed(KeyCode::Digit3) {
249
light.shadow_normal_bias -= normal_bias_step_size;
250
}
251
if input.just_pressed(KeyCode::Digit4) {
252
light.shadow_normal_bias += normal_bias_step_size;
253
}
254
if input.just_pressed(KeyCode::KeyR) {
255
light.shadow_depth_bias = PointLight::DEFAULT_SHADOW_DEPTH_BIAS;
256
light.shadow_normal_bias = PointLight::DEFAULT_SHADOW_NORMAL_BIAS;
257
}
258
if input.just_pressed(KeyCode::KeyZ) {
259
light.shadow_depth_bias = 0.0;
260
light.shadow_normal_bias = 0.0;
261
}
262
263
*writer.text(*example_text, 10) = format!("{:.2}", light.shadow_depth_bias);
264
*writer.text(*example_text, 13) = format!("{:.1}", light.shadow_normal_bias);
265
}
266
}
267
268
fn adjust_directional_light_biases(
269
input: Res<ButtonInput<KeyCode>>,
270
mut query: Query<&mut DirectionalLight>,
271
example_text: Single<Entity, With<Text>>,
272
mut writer: TextUiWriter,
273
) {
274
let depth_bias_step_size = 0.01;
275
let normal_bias_step_size = 0.1;
276
for mut light in &mut query {
277
if input.just_pressed(KeyCode::Digit5) {
278
light.shadow_depth_bias -= depth_bias_step_size;
279
}
280
if input.just_pressed(KeyCode::Digit6) {
281
light.shadow_depth_bias += depth_bias_step_size;
282
}
283
if input.just_pressed(KeyCode::Digit7) {
284
light.shadow_normal_bias -= normal_bias_step_size;
285
}
286
if input.just_pressed(KeyCode::Digit8) {
287
light.shadow_normal_bias += normal_bias_step_size;
288
}
289
if input.just_pressed(KeyCode::KeyR) {
290
light.shadow_depth_bias = DirectionalLight::DEFAULT_SHADOW_DEPTH_BIAS;
291
light.shadow_normal_bias = DirectionalLight::DEFAULT_SHADOW_NORMAL_BIAS;
292
}
293
if input.just_pressed(KeyCode::KeyZ) {
294
light.shadow_depth_bias = 0.0;
295
light.shadow_normal_bias = 0.0;
296
}
297
298
*writer.text(*example_text, 16) = format!("{:.2}", light.shadow_depth_bias);
299
*writer.text(*example_text, 19) = format!("{:.1}", light.shadow_normal_bias);
300
}
301
}
302
303