Use load_with_settings instead of manually overriding srgbness in examples (#13399)
# Objective
`parallax_mapping` and `deferred_rendering` both use a roundabout way of
manually overriding the srgbness of their normal map textures.
This can now be done with `load_with_settings` in one line of code.
## Solution
- Delete the override systems and use `load_with_settings` instead
- Make `deferred_rendering`'s instruction text style consistent with
other examples while I'm in there.
(see #8478)
## Testing
Tested by running with `load` instead of `load_settings` and confirming
that lighting looks bad when `is_srgb` is not configured, and good when
it is.
## Discussion
It would arguably make more sense to configure this in a `.meta` file,
but I used `load_with_settings` because that's how it was done in the
`clearcoat` example and it does seem nice for documentation purposes to
call this out explicitly in code.
This commit is contained in:
parent
aa907d5437
commit
7cbc0357be
@ -12,7 +12,7 @@ use bevy::{
|
|||||||
NotShadowCaster, NotShadowReceiver, OpaqueRendererMethod,
|
NotShadowCaster, NotShadowReceiver, OpaqueRendererMethod,
|
||||||
},
|
},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
render::render_resource::TextureFormat,
|
render::texture::ImageLoaderSettings,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
@ -21,13 +21,9 @@ fn main() {
|
|||||||
.insert_resource(DefaultOpaqueRendererMethod::deferred())
|
.insert_resource(DefaultOpaqueRendererMethod::deferred())
|
||||||
.insert_resource(DirectionalLightShadowMap { size: 4096 })
|
.insert_resource(DirectionalLightShadowMap { size: 4096 })
|
||||||
.add_plugins(DefaultPlugins)
|
.add_plugins(DefaultPlugins)
|
||||||
.insert_resource(Normal(None))
|
|
||||||
.insert_resource(Pause(true))
|
.insert_resource(Pause(true))
|
||||||
.add_systems(Startup, (setup, setup_parallax))
|
.add_systems(Startup, (setup, setup_parallax))
|
||||||
.add_systems(
|
.add_systems(Update, (animate_light_direction, switch_mode, spin))
|
||||||
Update,
|
|
||||||
(animate_light_direction, switch_mode, spin, update_normal),
|
|
||||||
)
|
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -211,14 +207,14 @@ fn setup(
|
|||||||
TextBundle::from_section(
|
TextBundle::from_section(
|
||||||
"",
|
"",
|
||||||
TextStyle {
|
TextStyle {
|
||||||
font_size: 18.0,
|
font_size: 20.0,
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.with_style(Style {
|
.with_style(Style {
|
||||||
position_type: PositionType::Absolute,
|
position_type: PositionType::Absolute,
|
||||||
top: Val::Px(10.0),
|
top: Val::Px(12.0),
|
||||||
left: Val::Px(10.0),
|
left: Val::Px(12.0),
|
||||||
..default()
|
..default()
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@ -244,14 +240,17 @@ fn setup_parallax(
|
|||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
mut materials: ResMut<Assets<StandardMaterial>>,
|
mut materials: ResMut<Assets<StandardMaterial>>,
|
||||||
mut meshes: ResMut<Assets<Mesh>>,
|
mut meshes: ResMut<Assets<Mesh>>,
|
||||||
mut normal: ResMut<Normal>,
|
|
||||||
asset_server: Res<AssetServer>,
|
asset_server: Res<AssetServer>,
|
||||||
) {
|
) {
|
||||||
// The normal map. Note that to generate it in the GIMP image editor, you should
|
// 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
|
// open the depth map, and do Filters → Generic → Normal Map
|
||||||
// You should enable the "flip X" checkbox.
|
// You should enable the "flip X" checkbox.
|
||||||
let normal_handle = asset_server.load("textures/parallax_example/cube_normal.png");
|
let normal_handle = asset_server.load_with_settings(
|
||||||
normal.0 = Some(normal_handle);
|
"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));
|
let mut cube = Mesh::from(Cuboid::new(0.15, 0.15, 0.15));
|
||||||
|
|
||||||
@ -262,7 +261,7 @@ fn setup_parallax(
|
|||||||
let parallax_material = materials.add(StandardMaterial {
|
let parallax_material = materials.add(StandardMaterial {
|
||||||
perceptual_roughness: 0.4,
|
perceptual_roughness: 0.4,
|
||||||
base_color_texture: Some(asset_server.load("textures/parallax_example/cube_color.png")),
|
base_color_texture: Some(asset_server.load("textures/parallax_example/cube_color.png")),
|
||||||
normal_map_texture: normal.0.clone(),
|
normal_map_texture: Some(normal_handle),
|
||||||
// The depth map is a greyscale texture where black is the highest level and
|
// The depth map is a greyscale texture where black is the highest level and
|
||||||
// white the lowest.
|
// white the lowest.
|
||||||
depth_map: Some(asset_server.load("textures/parallax_example/cube_depth.png")),
|
depth_map: Some(asset_server.load("textures/parallax_example/cube_depth.png")),
|
||||||
@ -281,28 +280,6 @@ fn setup_parallax(
|
|||||||
Spin { speed: 0.3 },
|
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)]
|
#[derive(Component)]
|
||||||
struct Spin {
|
struct Spin {
|
||||||
speed: f32,
|
speed: f32,
|
||||||
|
|||||||
@ -3,18 +3,16 @@
|
|||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use bevy::{prelude::*, render::render_resource::TextureFormat};
|
use bevy::{prelude::*, render::texture::ImageLoaderSettings};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
App::new()
|
App::new()
|
||||||
.add_plugins(DefaultPlugins)
|
.add_plugins(DefaultPlugins)
|
||||||
.insert_resource(Normal(None))
|
|
||||||
.add_systems(Startup, setup)
|
.add_systems(Startup, setup)
|
||||||
.add_systems(
|
.add_systems(
|
||||||
Update,
|
Update,
|
||||||
(
|
(
|
||||||
spin,
|
spin,
|
||||||
update_normal,
|
|
||||||
move_camera,
|
move_camera,
|
||||||
update_parallax_depth_scale,
|
update_parallax_depth_scale,
|
||||||
update_parallax_layers,
|
update_parallax_layers,
|
||||||
@ -200,14 +198,17 @@ fn setup(
|
|||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
mut materials: ResMut<Assets<StandardMaterial>>,
|
mut materials: ResMut<Assets<StandardMaterial>>,
|
||||||
mut meshes: ResMut<Assets<Mesh>>,
|
mut meshes: ResMut<Assets<Mesh>>,
|
||||||
mut normal: ResMut<Normal>,
|
|
||||||
asset_server: Res<AssetServer>,
|
asset_server: Res<AssetServer>,
|
||||||
) {
|
) {
|
||||||
// The normal map. Note that to generate it in the GIMP image editor, you should
|
// 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
|
// open the depth map, and do Filters → Generic → Normal Map
|
||||||
// You should enable the "flip X" checkbox.
|
// You should enable the "flip X" checkbox.
|
||||||
let normal_handle = asset_server.load("textures/parallax_example/cube_normal.png");
|
let normal_handle = asset_server.load_with_settings(
|
||||||
normal.0 = Some(normal_handle);
|
"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,
|
||||||
|
);
|
||||||
|
|
||||||
// Camera
|
// Camera
|
||||||
commands.spawn((
|
commands.spawn((
|
||||||
@ -254,7 +255,7 @@ fn setup(
|
|||||||
let parallax_material = materials.add(StandardMaterial {
|
let parallax_material = materials.add(StandardMaterial {
|
||||||
perceptual_roughness: 0.4,
|
perceptual_roughness: 0.4,
|
||||||
base_color_texture: Some(asset_server.load("textures/parallax_example/cube_color.png")),
|
base_color_texture: Some(asset_server.load("textures/parallax_example/cube_color.png")),
|
||||||
normal_map_texture: normal.0.clone(),
|
normal_map_texture: Some(normal_handle),
|
||||||
// The depth map is a greyscale texture where black is the highest level and
|
// The depth map is a greyscale texture where black is the highest level and
|
||||||
// white the lowest.
|
// white the lowest.
|
||||||
depth_map: Some(asset_server.load("textures/parallax_example/cube_depth.png")),
|
depth_map: Some(asset_server.load("textures/parallax_example/cube_depth.png")),
|
||||||
@ -335,38 +336,3 @@ fn setup(
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Store handle of the normal to later modify its format in [`update_normal`].
|
|
||||||
#[derive(Resource)]
|
|
||||||
struct Normal(Option<Handle<Image>>);
|
|
||||||
|
|
||||||
/// Work around the default bevy image loader.
|
|
||||||
///
|
|
||||||
/// The bevy image loader used by `AssetServer` always loads images in
|
|
||||||
/// `Srgb` mode, which is usually what it should do,
|
|
||||||
/// but is incompatible with normal maps.
|
|
||||||
///
|
|
||||||
/// Normal maps require a texture in linear color space,
|
|
||||||
/// so we overwrite the format of the normal map we loaded through `AssetServer`
|
|
||||||
/// in this system.
|
|
||||||
///
|
|
||||||
/// Note that this method of conversion is a last resort workaround. You should
|
|
||||||
/// get your normal maps from a 3d model file, like gltf.
|
|
||||||
///
|
|
||||||
/// In this system, we wait until the image is loaded, immediately
|
|
||||||
/// change its format and never run the logic afterward.
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user