Skip to content

Instantly share code, notes, and snippets.

@DGriffin91
Created July 24, 2023 07:41
Show Gist options
  • Save DGriffin91/232da7eca8cf819c1478cb3053cd5079 to your computer and use it in GitHub Desktop.
Save DGriffin91/232da7eca8cf819c1478cb3053cd5079 to your computer and use it in GitHub Desktop.
Normals test for deferred PR
//! This example compares Forward, Forward + Prepass, and Deferred rendering.
use std::f32::consts::*;
use bevy::{
core_pipeline::{
fxaa::Fxaa,
prepass::{DeferredPrepass, DepthPrepass, MotionVectorPrepass, NormalPrepass},
},
pbr::{CascadeShadowConfigBuilder, DirectionalLightShadowMap},
pbr::{DefaultOpaqueRendererMethod, NotShadowCaster, OpaqueRendererMethod},
prelude::*,
render::render_resource::TextureFormat,
};
fn main() {
App::new()
.insert_resource(Msaa::Off)
.insert_resource(DefaultOpaqueRendererMethod(OpaqueRendererMethod::Deferred))
.insert_resource(ClearColor(Color::rgb_linear(0.05, 0.05, 0.05)))
.insert_resource(AmbientLight {
color: Color::WHITE,
brightness: 0.0,
})
.insert_resource(DirectionalLightShadowMap { size: 4096 })
.add_plugins(DefaultPlugins)
.insert_resource(Normal(None))
.insert_resource(Pause(true))
.add_systems(Startup, (setup, setup_parallax))
.add_systems(
Update,
(
animate_light_direction,
switch_mode,
spin,
update_normal,
camera_controller,
swap_material,
),
)
.run();
}
fn setup(
mut commands: Commands,
asset_server: Res<AssetServer>,
mut materials: ResMut<Assets<StandardMaterial>>,
mut meshes: ResMut<Assets<Mesh>>,
) {
commands.spawn((
Camera3dBundle {
camera: Camera {
//hdr: true,
..default()
},
transform: Transform::from_xyz(0.7, 0.7, 1.0)
.looking_at(Vec3::new(0.0, 0.3, 0.0), Vec3::Y),
..default()
},
FogSettings {
color: Color::rgba(0.05, 0.05, 0.05, 1.0),
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"),
},
DepthPrepass,
MotionVectorPrepass,
DeferredPrepass,
Fxaa::default(),
CameraController::default(),
));
commands.spawn(DirectionalLightBundle {
directional_light: DirectionalLight {
shadows_enabled: true,
..default()
},
cascade_shadow_config: CascadeShadowConfigBuilder {
num_cascades: 3,
maximum_distance: 10.0,
..default()
}
.into(),
transform: Transform::from_rotation(Quat::from_euler(EulerRot::ZYX, 0.0, 0.0, -FRAC_PI_4)),
..default()
});
// FlightHelmet
let helmet_scene = asset_server.load("models/FlightHelmet/FlightHelmet.gltf#Scene0");
commands.spawn(SceneBundle {
scene: helmet_scene.clone(),
..default()
});
commands.spawn(SceneBundle {
scene: helmet_scene,
transform: Transform::from_xyz(-3.0, 0.0, -3.0),
..default()
});
let shiney = StandardMaterial {
metallic: 1.0,
reflectance: 1.0,
perceptual_roughness: 0.0,
..default()
};
commands.spawn(Shiney(materials.add(shiney)));
let mut forward_mat: StandardMaterial = Color::rgb(0.1, 0.2, 0.1).into();
forward_mat.opaque_render_method = Some(OpaqueRendererMethod::Forward);
let forward_mat_h = materials.add(forward_mat);
// Plane
commands.spawn(PbrBundle {
mesh: meshes.add(shape::Plane::from_size(50.0).into()),
material: forward_mat_h.clone(),
..default()
});
let cube_h = meshes.add(Mesh::from(shape::Cube { size: 0.1 }));
let sphere_h = meshes.add(Mesh::from(shape::UVSphere {
radius: 0.125,
sectors: 128,
stacks: 128,
}));
// Cubes
commands.spawn(PbrBundle {
mesh: cube_h.clone(),
material: forward_mat_h.clone(),
transform: Transform::from_xyz(-0.3, 0.5, -0.2),
..default()
});
commands.spawn(PbrBundle {
mesh: cube_h,
material: forward_mat_h,
transform: Transform::from_xyz(0.2, 0.5, 0.2),
..default()
});
let sphere_color = Color::rgb(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((
PbrBundle {
mesh: sphere_h.clone(),
material: materials.add(unlit_mat),
transform: sphere_pos,
..default()
},
NotShadowCaster,
));
// Light
commands.spawn(PointLightBundle {
point_light: PointLight {
intensity: 1.0,
radius: 0.125,
shadows_enabled: true,
color: sphere_color,
..default()
},
transform: sphere_pos,
..default()
});
// 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::rgb(s_val, s_val, 1.0),
perceptual_roughness: 0.089,
metallic: 0.0,
..default()
})
} else if j == 1 {
materials.add(StandardMaterial {
base_color: Color::rgb(s_val, 1.0, s_val),
perceptual_roughness: 0.089,
metallic: 0.0,
..default()
})
} else {
materials.add(StandardMaterial {
base_color: Color::rgb(1.0, s_val, s_val),
perceptual_roughness: 0.089,
metallic: 0.0,
..default()
})
};
commands.spawn(PbrBundle {
mesh: sphere_h.clone(),
material,
transform: 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,
),
..default()
});
}
// Example instructions
commands.spawn(
TextBundle::from_section(
"",
TextStyle {
font_size: 18.0,
color: Color::WHITE,
..default()
},
)
.with_style(Style {
position_type: PositionType::Absolute,
top: Val::Px(10.0),
left: Val::Px(10.0),
..default()
}),
);
}
#[derive(Resource)]
struct Pause(bool);
fn animate_light_direction(
time: Res<Time>,
mut query: Query<&mut Transform, With<DirectionalLight>>,
pause: Res<Pause>,
) {
if pause.0 {
return;
}
for mut transform in &mut query {
transform.rotation = Quat::from_euler(
EulerRot::ZYX,
0.0,
time.elapsed_seconds() * PI / 5.0,
-FRAC_PI_4,
);
}
}
fn setup_parallax(
mut commands: Commands,
mut materials: ResMut<Assets<StandardMaterial>>,
mut meshes: ResMut<Assets<Mesh>>,
mut normal: ResMut<Normal>,
asset_server: Res<AssetServer>,
) {
// 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("textures/parallax_example/cube_normal.png");
normal.0 = Some(normal_handle);
let mut cube: Mesh = shape::Cube { size: 0.15 }.into();
// 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: normal.0.clone(),
// The depth map is a greyscale 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: 5.0f32.exp2(),
..default()
});
commands.spawn((
PbrBundle {
mesh: meshes.add(cube),
material: parallax_material,
transform: Transform::from_xyz(0.4, 0.2, -0.8),
..default()
},
Spin { speed: 0.3 },
));
}
/// Store handle of the normal to later modify its format in [`update_normal`].
#[derive(Resource)]
struct Normal(Option<Handle<Image>>);
// See `examples/3d/parallax_mapping.rs` example for reasoning
fn update_normal(
mut already_ran: Local<bool>,
mut images: ResMut<Assets<Image>>,
normal: Res<Normal>,
) {
if *already_ran {
return;
}
if let Some(normal) = normal.0.as_ref() {
if let Some(image) = images.get_mut(normal) {
image.texture_descriptor.format = TextureFormat::Rgba8Unorm;
*already_ran = true;
}
}
}
#[derive(Component)]
struct Spin {
speed: f32,
}
fn spin(time: Res<Time>, mut query: Query<(&mut Transform, &Spin)>, pause: Res<Pause>) {
if pause.0 {
return;
}
for (mut transform, spin) in query.iter_mut() {
transform.rotate_local_y(spin.speed * time.delta_seconds());
transform.rotate_local_x(spin.speed * time.delta_seconds());
transform.rotate_local_z(-spin.speed * time.delta_seconds());
}
}
#[derive(Resource, Default)]
enum DefaultRenderMode {
#[default]
Deferred,
Forward,
ForwardPrepass,
}
#[allow(clippy::too_many_arguments)]
fn switch_mode(
mut text: Query<&mut Text>,
mut commands: Commands,
keys: Res<Input<KeyCode>>,
mut default_opaque_renderer_method: ResMut<DefaultOpaqueRendererMethod>,
mut materials: ResMut<Assets<StandardMaterial>>,
cameras: Query<Entity, With<Camera>>,
mut pause: ResMut<Pause>,
mut hide_ui: Local<bool>,
mut mode: Local<DefaultRenderMode>,
) {
let mut text = text.single_mut();
let text = &mut text.sections[0].value;
text.clear();
if keys.just_pressed(KeyCode::Space) {
pause.0 = !pause.0;
}
if keys.just_pressed(KeyCode::Key1) {
*mode = DefaultRenderMode::Deferred;
default_opaque_renderer_method.0 = OpaqueRendererMethod::Deferred;
println!("DefaultOpaqueRendererMethod: Deferred");
for _ in materials.iter_mut() {}
for camera in &cameras {
commands.entity(camera).remove::<NormalPrepass>();
commands.entity(camera).insert(DepthPrepass);
commands.entity(camera).insert(MotionVectorPrepass);
commands.entity(camera).insert(DeferredPrepass);
}
}
if keys.just_pressed(KeyCode::Key2) {
*mode = DefaultRenderMode::Forward;
default_opaque_renderer_method.0 = OpaqueRendererMethod::Forward;
println!("DefaultOpaqueRendererMethod: Forward");
for _ in materials.iter_mut() {}
for camera in &cameras {
commands.entity(camera).remove::<NormalPrepass>();
commands.entity(camera).remove::<DepthPrepass>();
commands.entity(camera).remove::<MotionVectorPrepass>();
commands.entity(camera).remove::<DeferredPrepass>();
}
}
if keys.just_pressed(KeyCode::Key3) {
*mode = DefaultRenderMode::ForwardPrepass;
default_opaque_renderer_method.0 = OpaqueRendererMethod::Forward;
println!("DefaultOpaqueRendererMethod: Forward + Prepass");
for _ in materials.iter_mut() {}
for camera in &cameras {
commands.entity(camera).insert(NormalPrepass);
commands.entity(camera).insert(DepthPrepass);
commands.entity(camera).insert(MotionVectorPrepass);
commands.entity(camera).remove::<DeferredPrepass>();
}
}
if keys.just_pressed(KeyCode::H) {
*hide_ui = !*hide_ui;
}
if !*hide_ui {
text.push_str("(H) Hide UI\n");
text.push_str("(Space) Play/Pause\n\n");
text.push_str("Rendering Method:\n");
text.push_str(&format!(
"(1) {} Deferred\n",
if let DefaultRenderMode::Deferred = *mode {
">"
} else {
""
}
));
text.push_str(&format!(
"(2) {} Forward\n",
if let DefaultRenderMode::Forward = *mode {
">"
} else {
""
}
));
text.push_str(&format!(
"(3) {} Forward + Prepass\n",
if let DefaultRenderMode::ForwardPrepass = *mode {
">"
} else {
""
}
));
}
}
#[derive(Component)]
struct Shiney(Handle<StandardMaterial>);
#[derive(Component)]
struct ShineyIsSet;
fn swap_material(
mut commands: Commands,
mut has_standard_mat: Query<(Entity, &mut Handle<StandardMaterial>), Without<ShineyIsSet>>,
shiney_mat: Query<&Shiney>,
) {
let shiney_mat = shiney_mat.single();
for (entity, mut m) in has_standard_mat.iter_mut() {
*m = shiney_mat.0.clone();
commands.entity(entity).insert(ShineyIsSet);
}
}
#[derive(Component)]
pub struct CameraController {
pub enabled: bool,
pub initialized: bool,
pub sensitivity: f32,
pub key_forward: KeyCode,
pub key_back: KeyCode,
pub key_left: KeyCode,
pub key_right: KeyCode,
pub key_up: KeyCode,
pub key_down: KeyCode,
pub key_run: KeyCode,
pub mouse_key_enable_mouse: MouseButton,
pub keyboard_key_enable_mouse: KeyCode,
pub walk_speed: f32,
pub run_speed: f32,
pub friction: f32,
pub pitch: f32,
pub yaw: f32,
pub velocity: Vec3,
}
impl Default for CameraController {
fn default() -> Self {
Self {
enabled: true,
initialized: false,
sensitivity: 0.5,
key_forward: KeyCode::W,
key_back: KeyCode::S,
key_left: KeyCode::A,
key_right: KeyCode::D,
key_up: KeyCode::E,
key_down: KeyCode::Q,
key_run: KeyCode::ShiftLeft,
mouse_key_enable_mouse: MouseButton::Left,
keyboard_key_enable_mouse: KeyCode::M,
walk_speed: 0.5,
run_speed: 3.0,
friction: 0.5,
pitch: 0.0,
yaw: 0.0,
velocity: Vec3::ZERO,
}
}
}
pub fn camera_controller(
time: Res<Time>,
mut mouse_events: EventReader<bevy::input::mouse::MouseMotion>,
mouse_button_input: Res<Input<MouseButton>>,
key_input: Res<Input<KeyCode>>,
mut move_toggled: Local<bool>,
mut query: Query<(&mut Transform, &mut CameraController), With<Camera>>,
) {
let dt = time.delta_seconds();
if let Ok((mut transform, mut options)) = query.get_single_mut() {
if !options.initialized {
let (yaw, pitch, _roll) = transform.rotation.to_euler(EulerRot::YXZ);
options.yaw = yaw;
options.pitch = pitch;
options.initialized = true;
}
if !options.enabled {
return;
}
// Handle key input
let mut axis_input = Vec3::ZERO;
if key_input.pressed(options.key_forward) {
axis_input.z += 1.0;
}
if key_input.pressed(options.key_back) {
axis_input.z -= 1.0;
}
if key_input.pressed(options.key_right) {
axis_input.x += 1.0;
}
if key_input.pressed(options.key_left) {
axis_input.x -= 1.0;
}
if key_input.pressed(options.key_up) {
axis_input.y += 1.0;
}
if key_input.pressed(options.key_down) {
axis_input.y -= 1.0;
}
if key_input.just_pressed(options.keyboard_key_enable_mouse) {
*move_toggled = !*move_toggled;
}
// Apply movement update
if axis_input != Vec3::ZERO {
let max_speed = if key_input.pressed(options.key_run) {
options.run_speed
} else {
options.walk_speed
};
options.velocity = axis_input.normalize() * max_speed;
} else {
let friction = options.friction.clamp(0.0, 1.0);
options.velocity *= 1.0 - friction;
if options.velocity.length_squared() < 1e-6 {
options.velocity = Vec3::ZERO;
}
}
let forward = transform.forward();
let right = transform.right();
transform.translation += options.velocity.x * dt * right
+ options.velocity.y * dt * Vec3::Y
+ options.velocity.z * dt * forward;
// Handle mouse input
let mut mouse_delta = Vec2::ZERO;
if mouse_button_input.pressed(options.mouse_key_enable_mouse) || *move_toggled {
for mouse_event in mouse_events.iter() {
mouse_delta += mouse_event.delta;
}
}
if mouse_delta != Vec2::ZERO {
// Apply look update
options.pitch = (options.pitch - mouse_delta.y * 0.5 * options.sensitivity * dt)
.clamp(-PI / 2., PI / 2.);
options.yaw -= mouse_delta.x * options.sensitivity * dt;
transform.rotation = Quat::from_euler(EulerRot::ZYX, 0.0, options.yaw, options.pitch);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment