1use std::f32::consts::*;
4
5use 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
17fn 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
28fn 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::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 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 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 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 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 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 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 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 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)]
196struct Pause(bool);
197
198fn 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
211fn 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 let normal_handle = asset_server.load_with_settings(
221 "textures/parallax_example/cube_normal.png",
222 |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 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 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)]
253struct Spin {
254 speed: f32,
255}
256
257fn 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)]
269enum DefaultRenderMode {
270 #[default]
271 Deferred,
272 Forward,
273 ForwardPrepass,
274}
275
276fn 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}