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