Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/examples/3d/deferred_rendering.rs
6592 views
1
//! This example compares Forward, Forward + Prepass, and Deferred rendering.
2
3
use std::f32::consts::*;
4
5
use bevy::{
6
anti_alias::fxaa::Fxaa,
7
core_pipeline::prepass::{DeferredPrepass, DepthPrepass, MotionVectorPrepass, NormalPrepass},
8
image::ImageLoaderSettings,
9
light::{
10
CascadeShadowConfigBuilder, DirectionalLightShadowMap, NotShadowCaster, NotShadowReceiver,
11
},
12
math::ops,
13
pbr::{DefaultOpaqueRendererMethod, OpaqueRendererMethod},
14
prelude::*,
15
};
16
17
fn main() {
18
App::new()
19
.insert_resource(DefaultOpaqueRendererMethod::deferred())
20
.insert_resource(DirectionalLightShadowMap { size: 4096 })
21
.add_plugins(DefaultPlugins)
22
.insert_resource(Pause(true))
23
.add_systems(Startup, (setup, setup_parallax))
24
.add_systems(Update, (animate_light_direction, switch_mode, spin))
25
.run();
26
}
27
28
fn setup(
29
mut commands: Commands,
30
asset_server: Res<AssetServer>,
31
mut materials: ResMut<Assets<StandardMaterial>>,
32
mut meshes: ResMut<Assets<Mesh>>,
33
) {
34
commands.spawn((
35
Camera3d::default(),
36
Transform::from_xyz(0.7, 0.7, 1.0).looking_at(Vec3::new(0.0, 0.3, 0.0), Vec3::Y),
37
// MSAA needs to be off for Deferred rendering
38
Msaa::Off,
39
DistanceFog {
40
color: Color::srgb_u8(43, 44, 47),
41
falloff: FogFalloff::Linear {
42
start: 1.0,
43
end: 8.0,
44
},
45
..default()
46
},
47
EnvironmentMapLight {
48
diffuse_map: asset_server.load("environment_maps/pisa_diffuse_rgb9e5_zstd.ktx2"),
49
specular_map: asset_server.load("environment_maps/pisa_specular_rgb9e5_zstd.ktx2"),
50
intensity: 2000.0,
51
..default()
52
},
53
DepthPrepass,
54
MotionVectorPrepass,
55
DeferredPrepass,
56
Fxaa::default(),
57
));
58
59
commands.spawn((
60
DirectionalLight {
61
illuminance: 15_000.,
62
shadows_enabled: true,
63
..default()
64
},
65
CascadeShadowConfigBuilder {
66
num_cascades: 3,
67
maximum_distance: 10.0,
68
..default()
69
}
70
.build(),
71
Transform::from_rotation(Quat::from_euler(EulerRot::ZYX, 0.0, 0.0, -FRAC_PI_4)),
72
));
73
74
// FlightHelmet
75
let helmet_scene = asset_server
76
.load(GltfAssetLabel::Scene(0).from_asset("models/FlightHelmet/FlightHelmet.gltf"));
77
78
commands.spawn(SceneRoot(helmet_scene.clone()));
79
commands.spawn((
80
SceneRoot(helmet_scene),
81
Transform::from_xyz(-4.0, 0.0, -3.0),
82
));
83
84
let mut forward_mat: StandardMaterial = Color::srgb(0.1, 0.2, 0.1).into();
85
forward_mat.opaque_render_method = OpaqueRendererMethod::Forward;
86
let forward_mat_h = materials.add(forward_mat);
87
88
// Plane
89
commands.spawn((
90
Mesh3d(meshes.add(Plane3d::default().mesh().size(50.0, 50.0))),
91
MeshMaterial3d(forward_mat_h.clone()),
92
));
93
94
let cube_h = meshes.add(Cuboid::new(0.1, 0.1, 0.1));
95
let sphere_h = meshes.add(Sphere::new(0.125).mesh().uv(32, 18));
96
97
// Cubes
98
commands.spawn((
99
Mesh3d(cube_h.clone()),
100
MeshMaterial3d(forward_mat_h.clone()),
101
Transform::from_xyz(-0.3, 0.5, -0.2),
102
));
103
commands.spawn((
104
Mesh3d(cube_h),
105
MeshMaterial3d(forward_mat_h),
106
Transform::from_xyz(0.2, 0.5, 0.2),
107
));
108
109
let sphere_color = Color::srgb(10.0, 4.0, 1.0);
110
let sphere_pos = Transform::from_xyz(0.4, 0.5, -0.8);
111
// Emissive sphere
112
let mut unlit_mat: StandardMaterial = sphere_color.into();
113
unlit_mat.unlit = true;
114
commands.spawn((
115
Mesh3d(sphere_h.clone()),
116
MeshMaterial3d(materials.add(unlit_mat)),
117
sphere_pos,
118
NotShadowCaster,
119
));
120
// Light
121
commands.spawn((
122
PointLight {
123
intensity: 800.0,
124
radius: 0.125,
125
shadows_enabled: true,
126
color: sphere_color,
127
..default()
128
},
129
sphere_pos,
130
));
131
132
// Spheres
133
for i in 0..6 {
134
let j = i % 3;
135
let s_val = if i < 3 { 0.0 } else { 0.2 };
136
let material = if j == 0 {
137
materials.add(StandardMaterial {
138
base_color: Color::srgb(s_val, s_val, 1.0),
139
perceptual_roughness: 0.089,
140
metallic: 0.0,
141
..default()
142
})
143
} else if j == 1 {
144
materials.add(StandardMaterial {
145
base_color: Color::srgb(s_val, 1.0, s_val),
146
perceptual_roughness: 0.089,
147
metallic: 0.0,
148
..default()
149
})
150
} else {
151
materials.add(StandardMaterial {
152
base_color: Color::srgb(1.0, s_val, s_val),
153
perceptual_roughness: 0.089,
154
metallic: 0.0,
155
..default()
156
})
157
};
158
commands.spawn((
159
Mesh3d(sphere_h.clone()),
160
MeshMaterial3d(material),
161
Transform::from_xyz(
162
j as f32 * 0.25 + if i < 3 { -0.15 } else { 0.15 } - 0.4,
163
0.125,
164
-j as f32 * 0.25 + if i < 3 { -0.15 } else { 0.15 } + 0.4,
165
),
166
));
167
}
168
169
// sky
170
commands.spawn((
171
Mesh3d(meshes.add(Cuboid::new(2.0, 1.0, 1.0))),
172
MeshMaterial3d(materials.add(StandardMaterial {
173
base_color: Srgba::hex("888888").unwrap().into(),
174
unlit: true,
175
cull_mode: None,
176
..default()
177
})),
178
Transform::from_scale(Vec3::splat(1_000_000.0)),
179
NotShadowCaster,
180
NotShadowReceiver,
181
));
182
183
// Example instructions
184
commands.spawn((
185
Text::default(),
186
Node {
187
position_type: PositionType::Absolute,
188
top: px(12),
189
left: px(12),
190
..default()
191
},
192
));
193
}
194
195
#[derive(Resource)]
196
struct Pause(bool);
197
198
fn animate_light_direction(
199
time: Res<Time>,
200
mut query: Query<&mut Transform, With<DirectionalLight>>,
201
pause: Res<Pause>,
202
) {
203
if pause.0 {
204
return;
205
}
206
for mut transform in &mut query {
207
transform.rotate_y(time.delta_secs() * PI / 5.0);
208
}
209
}
210
211
fn setup_parallax(
212
mut commands: Commands,
213
mut materials: ResMut<Assets<StandardMaterial>>,
214
mut meshes: ResMut<Assets<Mesh>>,
215
asset_server: Res<AssetServer>,
216
) {
217
// The normal map. Note that to generate it in the GIMP image editor, you should
218
// open the depth map, and do Filters → Generic → Normal Map
219
// You should enable the "flip X" checkbox.
220
let normal_handle = asset_server.load_with_settings(
221
"textures/parallax_example/cube_normal.png",
222
// The normal map texture is in linear color space. Lighting won't look correct
223
// if `is_srgb` is `true`, which is the default.
224
|settings: &mut ImageLoaderSettings| settings.is_srgb = false,
225
);
226
227
let mut cube = Mesh::from(Cuboid::new(0.15, 0.15, 0.15));
228
229
// NOTE: for normal maps and depth maps to work, the mesh
230
// needs tangents generated.
231
cube.generate_tangents().unwrap();
232
233
let parallax_material = materials.add(StandardMaterial {
234
perceptual_roughness: 0.4,
235
base_color_texture: Some(asset_server.load("textures/parallax_example/cube_color.png")),
236
normal_map_texture: Some(normal_handle),
237
// The depth map is a grayscale texture where black is the highest level and
238
// white the lowest.
239
depth_map: Some(asset_server.load("textures/parallax_example/cube_depth.png")),
240
parallax_depth_scale: 0.09,
241
parallax_mapping_method: ParallaxMappingMethod::Relief { max_steps: 4 },
242
max_parallax_layer_count: ops::exp2(5.0f32),
243
..default()
244
});
245
commands.spawn((
246
Mesh3d(meshes.add(cube)),
247
MeshMaterial3d(parallax_material),
248
Transform::from_xyz(0.4, 0.2, -0.8),
249
Spin { speed: 0.3 },
250
));
251
}
252
#[derive(Component)]
253
struct Spin {
254
speed: f32,
255
}
256
257
fn spin(time: Res<Time>, mut query: Query<(&mut Transform, &Spin)>, pause: Res<Pause>) {
258
if pause.0 {
259
return;
260
}
261
for (mut transform, spin) in query.iter_mut() {
262
transform.rotate_local_y(spin.speed * time.delta_secs());
263
transform.rotate_local_x(spin.speed * time.delta_secs());
264
transform.rotate_local_z(-spin.speed * time.delta_secs());
265
}
266
}
267
268
#[derive(Resource, Default)]
269
enum DefaultRenderMode {
270
#[default]
271
Deferred,
272
Forward,
273
ForwardPrepass,
274
}
275
276
fn switch_mode(
277
mut text: Single<&mut Text>,
278
mut commands: Commands,
279
keys: Res<ButtonInput<KeyCode>>,
280
mut default_opaque_renderer_method: ResMut<DefaultOpaqueRendererMethod>,
281
mut materials: ResMut<Assets<StandardMaterial>>,
282
cameras: Query<Entity, With<Camera>>,
283
mut pause: ResMut<Pause>,
284
mut hide_ui: Local<bool>,
285
mut mode: Local<DefaultRenderMode>,
286
) {
287
text.clear();
288
289
if keys.just_pressed(KeyCode::Space) {
290
pause.0 = !pause.0;
291
}
292
293
if keys.just_pressed(KeyCode::Digit1) {
294
*mode = DefaultRenderMode::Deferred;
295
default_opaque_renderer_method.set_to_deferred();
296
println!("DefaultOpaqueRendererMethod: Deferred");
297
for _ in materials.iter_mut() {}
298
for camera in &cameras {
299
commands.entity(camera).remove::<NormalPrepass>();
300
commands.entity(camera).insert(DepthPrepass);
301
commands.entity(camera).insert(MotionVectorPrepass);
302
commands.entity(camera).insert(DeferredPrepass);
303
}
304
}
305
if keys.just_pressed(KeyCode::Digit2) {
306
*mode = DefaultRenderMode::Forward;
307
default_opaque_renderer_method.set_to_forward();
308
println!("DefaultOpaqueRendererMethod: Forward");
309
for _ in materials.iter_mut() {}
310
for camera in &cameras {
311
commands.entity(camera).remove::<NormalPrepass>();
312
commands.entity(camera).remove::<DepthPrepass>();
313
commands.entity(camera).remove::<MotionVectorPrepass>();
314
commands.entity(camera).remove::<DeferredPrepass>();
315
}
316
}
317
if keys.just_pressed(KeyCode::Digit3) {
318
*mode = DefaultRenderMode::ForwardPrepass;
319
default_opaque_renderer_method.set_to_forward();
320
println!("DefaultOpaqueRendererMethod: Forward + Prepass");
321
for _ in materials.iter_mut() {}
322
for camera in &cameras {
323
commands.entity(camera).insert(NormalPrepass);
324
commands.entity(camera).insert(DepthPrepass);
325
commands.entity(camera).insert(MotionVectorPrepass);
326
commands.entity(camera).remove::<DeferredPrepass>();
327
}
328
}
329
330
if keys.just_pressed(KeyCode::KeyH) {
331
*hide_ui = !*hide_ui;
332
}
333
334
if !*hide_ui {
335
text.push_str("(H) Hide UI\n");
336
text.push_str("(Space) Play/Pause\n\n");
337
text.push_str("Rendering Method:\n");
338
339
text.push_str(&format!(
340
"(1) {} Deferred\n",
341
if let DefaultRenderMode::Deferred = *mode {
342
">"
343
} else {
344
""
345
}
346
));
347
text.push_str(&format!(
348
"(2) {} Forward\n",
349
if let DefaultRenderMode::Forward = *mode {
350
">"
351
} else {
352
""
353
}
354
));
355
text.push_str(&format!(
356
"(3) {} Forward + Prepass\n",
357
if let DefaultRenderMode::ForwardPrepass = *mode {
358
">"
359
} else {
360
""
361
}
362
));
363
}
364
}
365
366