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 material::OpaqueRendererMethod,
13 math::ops,
14 pbr::DefaultOpaqueRendererMethod,
15 prelude::*,
16};
17
18fn 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
29fn 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::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 let helmet_scene = asset_server
77 .load(GltfAssetLabel::Scene(0).from_asset("models/FlightHelmet/FlightHelmet.gltf"));
78
79 commands.spawn(WorldAssetRoot(helmet_scene.clone()));
80 commands.spawn((
81 WorldAssetRoot(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 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 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 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 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 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 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 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)]
197struct Pause(bool);
198
199fn 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
212fn 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 let normal_handle = asset_server
222 .load_builder()
223 .with_settings(
224 |settings: &mut ImageLoaderSettings| settings.is_srgb = false,
227 )
228 .load("textures/parallax_example/cube_normal.png");
229
230 let mut cube = Mesh::from(Cuboid::new(0.15, 0.15, 0.15));
231
232 cube.generate_tangents().unwrap();
235
236 let parallax_material = materials.add(StandardMaterial {
237 perceptual_roughness: 0.4,
238 base_color_texture: Some(asset_server.load("textures/parallax_example/cube_color.png")),
239 normal_map_texture: Some(normal_handle),
240 depth_map: Some(asset_server.load("textures/parallax_example/cube_depth.png")),
243 parallax_depth_scale: 0.09,
244 parallax_mapping_method: ParallaxMappingMethod::Relief { max_steps: 4 },
245 max_parallax_layer_count: ops::exp2(5.0f32),
246 ..default()
247 });
248 commands.spawn((
249 Mesh3d(meshes.add(cube)),
250 MeshMaterial3d(parallax_material),
251 Transform::from_xyz(0.4, 0.2, -0.8),
252 Spin { speed: 0.3 },
253 ));
254}
255#[derive(Component)]
256struct Spin {
257 speed: f32,
258}
259
260fn spin(time: Res<Time>, mut query: Query<(&mut Transform, &Spin)>, pause: Res<Pause>) {
261 if pause.0 {
262 return;
263 }
264 for (mut transform, spin) in query.iter_mut() {
265 transform.rotate_local_y(spin.speed * time.delta_secs());
266 transform.rotate_local_x(spin.speed * time.delta_secs());
267 transform.rotate_local_z(-spin.speed * time.delta_secs());
268 }
269}
270
271#[derive(Resource, Default)]
272enum DefaultRenderMode {
273 #[default]
274 Deferred,
275 Forward,
276 ForwardPrepass,
277}
278
279fn switch_mode(
280 mut text: Single<&mut Text>,
281 mut commands: Commands,
282 keys: Res<ButtonInput<KeyCode>>,
283 mut default_opaque_renderer_method: ResMut<DefaultOpaqueRendererMethod>,
284 mut materials: ResMut<Assets<StandardMaterial>>,
285 cameras: Query<Entity, With<Camera>>,
286 mut pause: ResMut<Pause>,
287 mut hide_ui: Local<bool>,
288 mut mode: Local<DefaultRenderMode>,
289) {
290 text.clear();
291
292 if keys.just_pressed(KeyCode::Space) {
293 pause.0 = !pause.0;
294 }
295
296 if keys.just_pressed(KeyCode::Digit1) {
297 *mode = DefaultRenderMode::Deferred;
298 default_opaque_renderer_method.set_to_deferred();
299 println!("DefaultOpaqueRendererMethod: Deferred");
300 for _ in materials.iter_mut() {}
301 for camera in &cameras {
302 commands.entity(camera).remove::<NormalPrepass>();
303 commands.entity(camera).insert(DepthPrepass);
304 commands.entity(camera).insert(MotionVectorPrepass);
305 commands.entity(camera).insert(DeferredPrepass);
306 }
307 }
308 if keys.just_pressed(KeyCode::Digit2) {
309 *mode = DefaultRenderMode::Forward;
310 default_opaque_renderer_method.set_to_forward();
311 println!("DefaultOpaqueRendererMethod: Forward");
312 for _ in materials.iter_mut() {}
313 for camera in &cameras {
314 commands.entity(camera).remove::<NormalPrepass>();
315 commands.entity(camera).remove::<DepthPrepass>();
316 commands.entity(camera).remove::<MotionVectorPrepass>();
317 commands.entity(camera).remove::<DeferredPrepass>();
318 }
319 }
320 if keys.just_pressed(KeyCode::Digit3) {
321 *mode = DefaultRenderMode::ForwardPrepass;
322 default_opaque_renderer_method.set_to_forward();
323 println!("DefaultOpaqueRendererMethod: Forward + Prepass");
324 for _ in materials.iter_mut() {}
325 for camera in &cameras {
326 commands.entity(camera).insert(NormalPrepass);
327 commands.entity(camera).insert(DepthPrepass);
328 commands.entity(camera).insert(MotionVectorPrepass);
329 commands.entity(camera).remove::<DeferredPrepass>();
330 }
331 }
332
333 if keys.just_pressed(KeyCode::KeyH) {
334 *hide_ui = !*hide_ui;
335 }
336
337 if !*hide_ui {
338 text.push_str("(H) Hide UI\n");
339 text.push_str("(Space) Play/Pause\n\n");
340 text.push_str("Rendering Method:\n");
341
342 text.push_str(&format!(
343 "(1) {} Deferred\n",
344 if let DefaultRenderMode::Deferred = *mode {
345 ">"
346 } else {
347 ""
348 }
349 ));
350 text.push_str(&format!(
351 "(2) {} Forward\n",
352 if let DefaultRenderMode::Forward = *mode {
353 ">"
354 } else {
355 ""
356 }
357 ));
358 text.push_str(&format!(
359 "(3) {} Forward + Prepass\n",
360 if let DefaultRenderMode::ForwardPrepass = *mode {
361 ">"
362 } else {
363 ""
364 }
365 ));
366 }
367}