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