From 76f67ba384c5377e5cd984fd0d5c7ae34c0f6c97 Mon Sep 17 00:00:00 2001 From: Lucas Farias Date: Wed, 26 Feb 2025 10:48:40 -0300 Subject: [PATCH 1/5] Add scenes similar to `deferred_rendering` to testbed --- examples/testbed/3d.rs | 269 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 267 insertions(+), 2 deletions(-) diff --git a/examples/testbed/3d.rs b/examples/testbed/3d.rs index 5b35b945c5eef..df461751f215d 100644 --- a/examples/testbed/3d.rs +++ b/examples/testbed/3d.rs @@ -15,6 +15,15 @@ fn main() { .add_systems(OnEnter(Scene::Bloom), bloom::setup) .add_systems(OnEnter(Scene::Gltf), gltf::setup) .add_systems(OnEnter(Scene::Animation), animation::setup) + .add_systems(OnEnter(Scene::Forward), deferred::setup) + .add_systems( + OnEnter(Scene::ForwardPrepass), + (deferred::setup, deferred::forward_prepass_camera_setup).chain(), + ) + .add_systems( + OnEnter(Scene::Deferred), + (deferred::setup, deferred::deferred_camera_setup).chain(), + ) .add_systems(Update, switch_scene); #[cfg(feature = "bevy_ci_testing")] @@ -23,7 +32,7 @@ fn main() { app.run(); } -#[derive(Debug, Clone, Eq, PartialEq, Hash, States, Default)] +#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, States, Default)] #[states(scoped_entities)] enum Scene { #[default] @@ -31,6 +40,9 @@ enum Scene { Bloom, Gltf, Animation, + Forward, + ForwardPrepass, + Deferred, } impl Next for Scene { @@ -39,7 +51,10 @@ impl Next for Scene { Scene::Light => Scene::Bloom, Scene::Bloom => Scene::Gltf, Scene::Gltf => Scene::Animation, - Scene::Animation => Scene::Light, + Scene::Animation => Scene::Forward, + Scene::Forward => Scene::ForwardPrepass, + Scene::ForwardPrepass => Scene::Deferred, + Scene::Deferred => Scene::Light, } } } @@ -298,3 +313,253 @@ mod animation { } } } + +mod deferred { + use bevy::{ + asset::{AssetServer, Assets}, + color::{Color, Srgba}, + core_pipeline::{ + fxaa::Fxaa, + prepass::{DeferredPrepass, DepthPrepass, MotionVectorPrepass, NormalPrepass}, + }, + gltf::GltfAssetLabel, + math::{EulerRot, Quat, Vec3}, + pbr::{ + CascadeShadowConfigBuilder, DirectionalLight, DistanceFog, FogFalloff, MeshMaterial3d, + NotShadowCaster, NotShadowReceiver, OpaqueRendererMethod, ParallaxMappingMethod, + PointLight, StandardMaterial, + }, + prelude::{ + Camera, Camera3d, Commands, Cuboid, Entity, EnvironmentMapLight, Mesh, Mesh3d, + Meshable, Msaa, Plane3d, Res, ResMut, Single, Sphere, State, StateScoped, Transform, + With, + }, + scene::SceneRoot, + utils::default, + }; + use bevy_image::ImageLoaderSettings; + + pub fn setup( + mut commands: Commands, + asset_server: Res, + mut materials: ResMut>, + mut meshes: ResMut>, + scene: Res>, + ) { + commands.spawn(( + Camera3d::default(), + Camera { + // Deferred both supports both hdr: true and hdr: false + hdr: false, + ..default() + }, + Transform::from_xyz(0.7, 0.7, 1.0).looking_at(Vec3::new(0.0, 0.3, 0.0), Vec3::Y), + // MSAA needs to be off for Deferred rendering + Msaa::Off, + DistanceFog { + color: Color::srgb_u8(43, 44, 47), + falloff: FogFalloff::Linear { + start: 1.0, + end: 8.0, + }, + ..default() + }, + EnvironmentMapLight { + diffuse_map: asset_server.load("environment_maps/pisa_diffuse_rgb9e5_zstd.ktx2"), + specular_map: asset_server.load("environment_maps/pisa_specular_rgb9e5_zstd.ktx2"), + intensity: 2000.0, + ..default() + }, + Fxaa::default(), + StateScoped(*scene.get()), + )); + + commands.spawn(( + DirectionalLight { + illuminance: 15_000., + shadows_enabled: true, + ..default() + }, + CascadeShadowConfigBuilder { + num_cascades: 3, + maximum_distance: 10.0, + ..default() + } + .build(), + Transform::from_rotation(Quat::from_euler( + EulerRot::ZYX, + 0.0, + 0.0, + -std::f32::consts::FRAC_PI_4, + )), + StateScoped(*scene.get()), + )); + + // FlightHelmet + let helmet_scene = asset_server + .load(GltfAssetLabel::Scene(0).from_asset("models/FlightHelmet/FlightHelmet.gltf")); + + commands.spawn((SceneRoot(helmet_scene.clone()), StateScoped(*scene.get()))); + commands.spawn(( + SceneRoot(helmet_scene), + Transform::from_xyz(-4.0, 0.0, -3.0), + StateScoped(*scene.get()), + )); + + let mut forward_mat: StandardMaterial = Color::srgb(0.1, 0.2, 0.1).into(); + forward_mat.opaque_render_method = OpaqueRendererMethod::Forward; + let forward_mat_h = materials.add(forward_mat); + + // Plane + commands.spawn(( + Mesh3d(meshes.add(Plane3d::default().mesh().size(50.0, 50.0))), + MeshMaterial3d(forward_mat_h.clone()), + StateScoped(*scene.get()), + )); + + let cube_h = meshes.add(Cuboid::new(0.1, 0.1, 0.1)); + let sphere_h = meshes.add(Sphere::new(0.125).mesh().uv(32, 18)); + + // Cubes + commands.spawn(( + Mesh3d(cube_h.clone()), + MeshMaterial3d(forward_mat_h.clone()), + Transform::from_xyz(-0.3, 0.5, -0.2), + StateScoped(*scene.get()), + )); + commands.spawn(( + Mesh3d(cube_h), + MeshMaterial3d(forward_mat_h), + Transform::from_xyz(0.2, 0.5, 0.2), + StateScoped(*scene.get()), + )); + + let sphere_color = Color::srgb(10.0, 4.0, 1.0); + let sphere_pos = Transform::from_xyz(0.4, 0.5, -0.8); + // Emissive sphere + let mut unlit_mat: StandardMaterial = sphere_color.into(); + unlit_mat.unlit = true; + commands.spawn(( + Mesh3d(sphere_h.clone()), + MeshMaterial3d(materials.add(unlit_mat)), + sphere_pos, + NotShadowCaster, + StateScoped(*scene.get()), + )); + // Light + commands.spawn(( + PointLight { + intensity: 800.0, + radius: 0.125, + shadows_enabled: true, + color: sphere_color, + ..default() + }, + sphere_pos, + StateScoped(*scene.get()), + )); + + // Spheres + for i in 0..6 { + let j = i % 3; + let s_val = if i < 3 { 0.0 } else { 0.2 }; + let material = if j == 0 { + materials.add(StandardMaterial { + base_color: Color::srgb(s_val, s_val, 1.0), + perceptual_roughness: 0.089, + metallic: 0.0, + ..default() + }) + } else if j == 1 { + materials.add(StandardMaterial { + base_color: Color::srgb(s_val, 1.0, s_val), + perceptual_roughness: 0.089, + metallic: 0.0, + ..default() + }) + } else { + materials.add(StandardMaterial { + base_color: Color::srgb(1.0, s_val, s_val), + perceptual_roughness: 0.089, + metallic: 0.0, + ..default() + }) + }; + commands.spawn(( + Mesh3d(sphere_h.clone()), + MeshMaterial3d(material), + Transform::from_xyz( + j as f32 * 0.25 + if i < 3 { -0.15 } else { 0.15 } - 0.4, + 0.125, + -j as f32 * 0.25 + if i < 3 { -0.15 } else { 0.15 } + 0.4, + ), + StateScoped(*scene.get()), + )); + } + + // sky + commands.spawn(( + Mesh3d(meshes.add(Cuboid::new(2.0, 1.0, 1.0))), + MeshMaterial3d(materials.add(StandardMaterial { + base_color: Srgba::hex("888888").unwrap().into(), + unlit: true, + cull_mode: None, + ..default() + })), + Transform::from_scale(Vec3::splat(1_000_000.0)), + NotShadowCaster, + NotShadowReceiver, + StateScoped(*scene.get()), + )); + + // The normal map. Note that to generate it in the GIMP image editor, you should + // open the depth map, and do Filters → Generic → Normal Map + // You should enable the "flip X" checkbox. + let normal_handle = asset_server.load_with_settings( + "textures/parallax_example/cube_normal.png", + // The normal map texture is in linear color space. Lighting won't look correct + // if `is_srgb` is `true`, which is the default. + |settings: &mut ImageLoaderSettings| settings.is_srgb = false, + ); + + let mut cube = Mesh::from(Cuboid::new(0.15, 0.15, 0.15)); + + // NOTE: for normal maps and depth maps to work, the mesh + // needs tangents generated. + cube.generate_tangents().unwrap(); + + let parallax_material = materials.add(StandardMaterial { + perceptual_roughness: 0.4, + base_color_texture: Some(asset_server.load("textures/parallax_example/cube_color.png")), + normal_map_texture: Some(normal_handle), + // The depth map is a grayscale texture where black is the highest level and + // white the lowest. + depth_map: Some(asset_server.load("textures/parallax_example/cube_depth.png")), + parallax_depth_scale: 0.09, + parallax_mapping_method: ParallaxMappingMethod::Relief { max_steps: 4 }, + max_parallax_layer_count: bevy::math::ops::exp2(5.0f32), + ..default() + }); + commands.spawn(( + Mesh3d(meshes.add(cube)), + MeshMaterial3d(parallax_material), + Transform::from_xyz(0.4, 0.2, -0.8), + StateScoped(*scene.get()), + )); + } + + pub fn forward_prepass_camera_setup( + mut commands: Commands, + camera: Single>, + ) { + commands + .entity(*camera) + .insert((NormalPrepass, DepthPrepass, MotionVectorPrepass)); + } + + pub fn deferred_camera_setup(mut commands: Commands, camera: Single>) { + commands + .entity(*camera) + .insert((DepthPrepass, MotionVectorPrepass, DeferredPrepass)); + } +} From a860dfa6d5ea746fa28c2a713bb961a073ac0d96 Mon Sep 17 00:00:00 2001 From: Lucas Farias Date: Wed, 26 Feb 2025 11:21:55 -0300 Subject: [PATCH 2/5] Reduce the number of objects on deferred scenes of 3d testbed --- examples/testbed/3d.rs | 47 ++---------------------------------------- 1 file changed, 2 insertions(+), 45 deletions(-) diff --git a/examples/testbed/3d.rs b/examples/testbed/3d.rs index df461751f215d..98f042f1f7ddb 100644 --- a/examples/testbed/3d.rs +++ b/examples/testbed/3d.rs @@ -323,6 +323,7 @@ mod deferred { prepass::{DeferredPrepass, DepthPrepass, MotionVectorPrepass, NormalPrepass}, }, gltf::GltfAssetLabel, + image::ImageLoaderSettings, math::{EulerRot, Quat, Vec3}, pbr::{ CascadeShadowConfigBuilder, DirectionalLight, DistanceFog, FogFalloff, MeshMaterial3d, @@ -337,7 +338,6 @@ mod deferred { scene::SceneRoot, utils::default, }; - use bevy_image::ImageLoaderSettings; pub fn setup( mut commands: Commands, @@ -399,12 +399,7 @@ mod deferred { let helmet_scene = asset_server .load(GltfAssetLabel::Scene(0).from_asset("models/FlightHelmet/FlightHelmet.gltf")); - commands.spawn((SceneRoot(helmet_scene.clone()), StateScoped(*scene.get()))); - commands.spawn(( - SceneRoot(helmet_scene), - Transform::from_xyz(-4.0, 0.0, -3.0), - StateScoped(*scene.get()), - )); + commands.spawn((SceneRoot(helmet_scene), StateScoped(*scene.get()))); let mut forward_mat: StandardMaterial = Color::srgb(0.1, 0.2, 0.1).into(); forward_mat.opaque_render_method = OpaqueRendererMethod::Forward; @@ -459,44 +454,6 @@ mod deferred { StateScoped(*scene.get()), )); - // Spheres - for i in 0..6 { - let j = i % 3; - let s_val = if i < 3 { 0.0 } else { 0.2 }; - let material = if j == 0 { - materials.add(StandardMaterial { - base_color: Color::srgb(s_val, s_val, 1.0), - perceptual_roughness: 0.089, - metallic: 0.0, - ..default() - }) - } else if j == 1 { - materials.add(StandardMaterial { - base_color: Color::srgb(s_val, 1.0, s_val), - perceptual_roughness: 0.089, - metallic: 0.0, - ..default() - }) - } else { - materials.add(StandardMaterial { - base_color: Color::srgb(1.0, s_val, s_val), - perceptual_roughness: 0.089, - metallic: 0.0, - ..default() - }) - }; - commands.spawn(( - Mesh3d(sphere_h.clone()), - MeshMaterial3d(material), - Transform::from_xyz( - j as f32 * 0.25 + if i < 3 { -0.15 } else { 0.15 } - 0.4, - 0.125, - -j as f32 * 0.25 + if i < 3 { -0.15 } else { 0.15 } + 0.4, - ), - StateScoped(*scene.get()), - )); - } - // sky commands.spawn(( Mesh3d(meshes.add(Cuboid::new(2.0, 1.0, 1.0))), From dbffaee624f18d06832b037e167d2dd2d2f4c1e1 Mon Sep 17 00:00:00 2001 From: Lucas Farias Date: Wed, 26 Feb 2025 11:36:14 -0300 Subject: [PATCH 3/5] Add one more test to 3d testbed --- examples/testbed/3d.rs | 70 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 65 insertions(+), 5 deletions(-) diff --git a/examples/testbed/3d.rs b/examples/testbed/3d.rs index 98f042f1f7ddb..fc73be0031c38 100644 --- a/examples/testbed/3d.rs +++ b/examples/testbed/3d.rs @@ -24,7 +24,24 @@ fn main() { OnEnter(Scene::Deferred), (deferred::setup, deferred::deferred_camera_setup).chain(), ) - .add_systems(Update, switch_scene); + .add_systems( + OnEnter(Scene::RemovePrepass), + ( + deferred::setup, + deferred::deferred_camera_setup, + deferred::remove_prepass_timer_init, + ) + .chain(), + ) + .add_systems(Update, switch_scene) + .add_systems( + Update, + ( + deferred::remove_prepass.run_if(resource_removed::), + deferred::remove_prepass_timer_tick + .run_if(resource_exists::), + ), + ); #[cfg(feature = "bevy_ci_testing")] app.add_systems(Update, helpers::switch_scene_in_ci::); @@ -43,6 +60,7 @@ enum Scene { Forward, ForwardPrepass, Deferred, + RemovePrepass, } impl Next for Scene { @@ -54,7 +72,8 @@ impl Next for Scene { Scene::Animation => Scene::Forward, Scene::Forward => Scene::ForwardPrepass, Scene::ForwardPrepass => Scene::Deferred, - Scene::Deferred => Scene::Light, + Scene::Deferred => Scene::RemovePrepass, + Scene::RemovePrepass => Scene::Light, } } } @@ -331,14 +350,21 @@ mod deferred { PointLight, StandardMaterial, }, prelude::{ - Camera, Camera3d, Commands, Cuboid, Entity, EnvironmentMapLight, Mesh, Mesh3d, - Meshable, Msaa, Plane3d, Res, ResMut, Single, Sphere, State, StateScoped, Transform, - With, + Camera, Camera3d, Commands, Component, Cuboid, Deref, DerefMut, Entity, + EnvironmentMapLight, Mesh, Mesh3d, Meshable, Msaa, Plane3d, Res, ResMut, Resource, + Single, Sphere, State, StateScoped, Transform, With, }, scene::SceneRoot, + time::{Time, Timer}, utils::default, }; + #[derive(Resource, Deref, DerefMut)] + pub struct RemovePrepassTimer(Timer); + + #[derive(Component)] + pub struct ParallaxCube; + pub fn setup( mut commands: Commands, asset_server: Res, @@ -501,6 +527,7 @@ mod deferred { Mesh3d(meshes.add(cube)), MeshMaterial3d(parallax_material), Transform::from_xyz(0.4, 0.2, -0.8), + ParallaxCube, StateScoped(*scene.get()), )); } @@ -519,4 +546,37 @@ mod deferred { .entity(*camera) .insert((DepthPrepass, MotionVectorPrepass, DeferredPrepass)); } + + pub fn remove_prepass_timer_init(mut commands: Commands) { + commands.insert_resource(RemovePrepassTimer(Timer::from_seconds( + 0.5, + bevy::time::TimerMode::Once, + ))); + } + + pub fn remove_prepass_timer_tick( + mut commands: Commands, + time: Res