mirror of
https://github.com/bevyengine/bevy
synced 2024-11-26 14:40:19 +00:00
dd619a1087
# Objective After adding configurable exposure, we set the default ev100 value to `7` (indoor). This brought us out of sync with Blender's configuration and defaults. This PR changes the default to `9.7` (bright indoor or very overcast outdoors), as I calibrated in #11577. This feels like a very reasonable default. The other changes generally center around tweaking Bevy's lighting defaults and examples to play nicely with this number, alongside a few other tweaks and improvements. Note that for artistic reasons I have reverted some examples, which changed to directional lights in #11581, back to point lights. Fixes #11577 --- ## Changelog - Changed `Exposure::ev100` from `7` to `9.7` to better match Blender - Renamed `ExposureSettings` to `Exposure` - `Camera3dBundle` now includes `Exposure` for discoverability - Bumped `FULL_DAYLIGHT ` and `DIRECT_SUNLIGHT` to represent the middle-to-top of those ranges instead of near the bottom - Added new `AMBIENT_DAYLIGHT` constant and set that as the new `DirectionalLight` default illuminance. - `PointLight` and `SpotLight` now have a default `intensity` of 1,000,000 lumens. This makes them actually useful in the context of the new "semi-outdoor" exposure and puts them in the "cinema lighting" category instead of the "common household light" category. They are also reasonably close to the Blender default. - `AmbientLight` default has been bumped from `20` to `80`. ## Migration Guide - The increased `Exposure::ev100` means that all existing 3D lighting will need to be adjusted to match (DirectionalLights, PointLights, SpotLights, EnvironmentMapLights, etc). Or alternatively, you can adjust the `Exposure::ev100` on your cameras to work nicely with your current lighting values. If you are currently relying on default intensity values, you might need to change the intensity to achieve the same effect. Note that in Bevy 0.12, point/spot lights had a different hard coded ev100 value than directional lights. In Bevy 0.13, they use the same ev100, so if you have both in your scene, the _scale_ between these light types has changed and you will likely need to adjust one or both of them.
253 lines
8.4 KiB
Rust
253 lines
8.4 KiB
Rust
//! Bevy has an optional prepass that is controlled per-material. A prepass is a rendering pass that runs before the main pass.
|
|
//! It will optionally generate various view textures. Currently it supports depth, normal, and motion vector textures.
|
|
//! The textures are not generated for any material using alpha blending.
|
|
|
|
use bevy::{
|
|
core_pipeline::prepass::{DepthPrepass, MotionVectorPrepass, NormalPrepass},
|
|
pbr::{NotShadowCaster, PbrPlugin},
|
|
prelude::*,
|
|
reflect::TypePath,
|
|
render::render_resource::{AsBindGroup, ShaderRef, ShaderType},
|
|
};
|
|
|
|
fn main() {
|
|
App::new()
|
|
.add_plugins((
|
|
DefaultPlugins.set(PbrPlugin {
|
|
// The prepass is enabled by default on the StandardMaterial,
|
|
// but you can disable it if you need to.
|
|
//
|
|
// prepass_enabled: false,
|
|
..default()
|
|
}),
|
|
MaterialPlugin::<CustomMaterial>::default(),
|
|
MaterialPlugin::<PrepassOutputMaterial> {
|
|
// This material only needs to read the prepass textures,
|
|
// but the meshes using it should not contribute to the prepass render, so we can disable it.
|
|
prepass_enabled: false,
|
|
..default()
|
|
},
|
|
))
|
|
.add_systems(Startup, setup)
|
|
.add_systems(Update, (rotate, toggle_prepass_view))
|
|
// Disabling MSAA for maximum compatibility. Shader prepass with MSAA needs GPU capability MULTISAMPLED_SHADING
|
|
.insert_resource(Msaa::Off)
|
|
.run();
|
|
}
|
|
|
|
/// set up a simple 3D scene
|
|
fn setup(
|
|
mut commands: Commands,
|
|
mut meshes: ResMut<Assets<Mesh>>,
|
|
mut materials: ResMut<Assets<CustomMaterial>>,
|
|
mut std_materials: ResMut<Assets<StandardMaterial>>,
|
|
mut depth_materials: ResMut<Assets<PrepassOutputMaterial>>,
|
|
asset_server: Res<AssetServer>,
|
|
) {
|
|
// camera
|
|
commands.spawn((
|
|
Camera3dBundle {
|
|
transform: Transform::from_xyz(-2.0, 3., 5.0).looking_at(Vec3::ZERO, Vec3::Y),
|
|
..default()
|
|
},
|
|
// To enable the prepass you need to add the components associated with the ones you need
|
|
// This will write the depth buffer to a texture that you can use in the main pass
|
|
DepthPrepass,
|
|
// This will generate a texture containing world normals (with normal maps applied)
|
|
NormalPrepass,
|
|
// This will generate a texture containing screen space pixel motion vectors
|
|
MotionVectorPrepass,
|
|
));
|
|
|
|
// plane
|
|
commands.spawn(PbrBundle {
|
|
mesh: meshes.add(Plane3d::default().mesh().size(5.0, 5.0)),
|
|
material: std_materials.add(Color::rgb(0.3, 0.5, 0.3)),
|
|
..default()
|
|
});
|
|
|
|
// A quad that shows the outputs of the prepass
|
|
// To make it easy, we just draw a big quad right in front of the camera.
|
|
// For a real application, this isn't ideal.
|
|
commands.spawn((
|
|
MaterialMeshBundle {
|
|
mesh: meshes.add(Rectangle::new(20.0, 20.0)),
|
|
material: depth_materials.add(PrepassOutputMaterial {
|
|
settings: ShowPrepassSettings::default(),
|
|
}),
|
|
transform: Transform::from_xyz(-0.75, 1.25, 3.0)
|
|
.looking_at(Vec3::new(2.0, -2.5, -5.0), Vec3::Y),
|
|
..default()
|
|
},
|
|
NotShadowCaster,
|
|
));
|
|
|
|
// Opaque cube
|
|
commands.spawn((
|
|
MaterialMeshBundle {
|
|
mesh: meshes.add(Cuboid::default()),
|
|
material: materials.add(CustomMaterial {
|
|
color: Color::WHITE,
|
|
color_texture: Some(asset_server.load("branding/icon.png")),
|
|
alpha_mode: AlphaMode::Opaque,
|
|
}),
|
|
transform: Transform::from_xyz(-1.0, 0.5, 0.0),
|
|
..default()
|
|
},
|
|
Rotates,
|
|
));
|
|
|
|
// Cube with alpha mask
|
|
commands.spawn(PbrBundle {
|
|
mesh: meshes.add(Cuboid::default()),
|
|
material: std_materials.add(StandardMaterial {
|
|
alpha_mode: AlphaMode::Mask(1.0),
|
|
base_color_texture: Some(asset_server.load("branding/icon.png")),
|
|
..default()
|
|
}),
|
|
transform: Transform::from_xyz(0.0, 0.5, 0.0),
|
|
..default()
|
|
});
|
|
|
|
// Cube with alpha blending.
|
|
// Transparent materials are ignored by the prepass
|
|
commands.spawn(MaterialMeshBundle {
|
|
mesh: meshes.add(Cuboid::default()),
|
|
material: materials.add(CustomMaterial {
|
|
color: Color::WHITE,
|
|
color_texture: Some(asset_server.load("branding/icon.png")),
|
|
alpha_mode: AlphaMode::Blend,
|
|
}),
|
|
transform: Transform::from_xyz(1.0, 0.5, 0.0),
|
|
..default()
|
|
});
|
|
|
|
// light
|
|
commands.spawn(PointLightBundle {
|
|
point_light: PointLight {
|
|
shadows_enabled: true,
|
|
..default()
|
|
},
|
|
transform: Transform::from_xyz(4.0, 8.0, 4.0),
|
|
..default()
|
|
});
|
|
|
|
let style = TextStyle {
|
|
font_size: 18.0,
|
|
..default()
|
|
};
|
|
|
|
commands.spawn(
|
|
TextBundle::from_sections(vec![
|
|
TextSection::new("Prepass Output: transparent\n", style.clone()),
|
|
TextSection::new("\n\n", style.clone()),
|
|
TextSection::new("Controls\n", style.clone()),
|
|
TextSection::new("---------------\n", style.clone()),
|
|
TextSection::new("Space - Change output\n", style),
|
|
])
|
|
.with_style(Style {
|
|
position_type: PositionType::Absolute,
|
|
top: Val::Px(10.0),
|
|
left: Val::Px(10.0),
|
|
..default()
|
|
}),
|
|
);
|
|
}
|
|
|
|
// This is the struct that will be passed to your shader
|
|
#[derive(Asset, TypePath, AsBindGroup, Debug, Clone)]
|
|
struct CustomMaterial {
|
|
#[uniform(0)]
|
|
color: Color,
|
|
#[texture(1)]
|
|
#[sampler(2)]
|
|
color_texture: Option<Handle<Image>>,
|
|
alpha_mode: AlphaMode,
|
|
}
|
|
|
|
/// Not shown in this example, but if you need to specialize your material, the specialize
|
|
/// function will also be used by the prepass
|
|
impl Material for CustomMaterial {
|
|
fn fragment_shader() -> ShaderRef {
|
|
"shaders/custom_material.wgsl".into()
|
|
}
|
|
|
|
fn alpha_mode(&self) -> AlphaMode {
|
|
self.alpha_mode
|
|
}
|
|
|
|
// You can override the default shaders used in the prepass if your material does
|
|
// anything not supported by the default prepass
|
|
// fn prepass_fragment_shader() -> ShaderRef {
|
|
// "shaders/custom_material.wgsl".into()
|
|
// }
|
|
}
|
|
|
|
#[derive(Component)]
|
|
struct Rotates;
|
|
|
|
fn rotate(mut q: Query<&mut Transform, With<Rotates>>, time: Res<Time>) {
|
|
for mut t in q.iter_mut() {
|
|
let rot = (time.elapsed_seconds().sin() * 0.5 + 0.5) * std::f32::consts::PI * 2.0;
|
|
t.rotation = Quat::from_rotation_z(rot);
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Default, ShaderType)]
|
|
struct ShowPrepassSettings {
|
|
show_depth: u32,
|
|
show_normals: u32,
|
|
show_motion_vectors: u32,
|
|
padding_1: u32,
|
|
padding_2: u32,
|
|
}
|
|
|
|
// This shader simply loads the prepass texture and outputs it directly
|
|
#[derive(Asset, TypePath, AsBindGroup, Debug, Clone)]
|
|
struct PrepassOutputMaterial {
|
|
#[uniform(0)]
|
|
settings: ShowPrepassSettings,
|
|
}
|
|
|
|
impl Material for PrepassOutputMaterial {
|
|
fn fragment_shader() -> ShaderRef {
|
|
"shaders/show_prepass.wgsl".into()
|
|
}
|
|
|
|
// This needs to be transparent in order to show the scene behind the mesh
|
|
fn alpha_mode(&self) -> AlphaMode {
|
|
AlphaMode::Blend
|
|
}
|
|
}
|
|
|
|
/// Every time you press space, it will cycle between transparent, depth and normals view
|
|
fn toggle_prepass_view(
|
|
mut prepass_view: Local<u32>,
|
|
keycode: Res<ButtonInput<KeyCode>>,
|
|
material_handle: Query<&Handle<PrepassOutputMaterial>>,
|
|
mut materials: ResMut<Assets<PrepassOutputMaterial>>,
|
|
mut text: Query<&mut Text>,
|
|
) {
|
|
if keycode.just_pressed(KeyCode::Space) {
|
|
*prepass_view = (*prepass_view + 1) % 4;
|
|
|
|
let label = match *prepass_view {
|
|
0 => "transparent",
|
|
1 => "depth",
|
|
2 => "normals",
|
|
3 => "motion vectors",
|
|
_ => unreachable!(),
|
|
};
|
|
let mut text = text.single_mut();
|
|
text.sections[0].value = format!("Prepass Output: {label}\n");
|
|
for section in &mut text.sections {
|
|
section.style.color = Color::WHITE;
|
|
}
|
|
|
|
let handle = material_handle.single();
|
|
let mat = materials.get_mut(handle).unwrap();
|
|
mat.settings.show_depth = (*prepass_view == 1) as u32;
|
|
mat.settings.show_normals = (*prepass_view == 2) as u32;
|
|
mat.settings.show_motion_vectors = (*prepass_view == 3) as u32;
|
|
}
|
|
}
|