Add emissive_exposure_weight to the StandardMaterial (#13350)

# Objective

- The emissive color gets multiplied by the camera exposure value. But
this cancels out almost any emissive effect.
- Fixes #13133
- Closes PR #13337 

## Solution
- Add emissive_exposure_weight to the StandardMaterial
- In the shader this value is stored in the alpha channel of the
emissive color.
- This value defines how much the exposure influences the emissive
color.
- It's equal to Google's Filament:
https://google.github.io/filament/Materials.html#emissive

4f021583f1/shaders/src/shading_lit.fs (L287)

## Testing

- The result of
[EmissiveStrengthTest](https://github.com/KhronosGroup/glTF-Sample-Models/tree/main/2.0/EmissiveStrengthTest)
with the default value of 0.0:

without bloom:

![emissive_fix](https://github.com/bevyengine/bevy/assets/688816/8f8c131a-464a-4d7b-a9e4-4e28d679ee5d)

with bloom:

![emissive_fix_bloom](https://github.com/bevyengine/bevy/assets/688816/89f200ee-3bd5-4daa-bf64-8999b56df3fa)
This commit is contained in:
Johannes Hackel 2024-05-17 15:49:53 +02:00 committed by GitHub
parent 47d6e967b2
commit 1fcf6a444f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 24 additions and 18 deletions

View file

@ -90,6 +90,13 @@ pub struct StandardMaterial {
/// it just adds a value to the color seen on screen.
pub emissive: Color,
/// The weight in which the camera exposure influences the emissive color.
/// A value of `0.0` means the emissive color is not affected by the camera exposure.
/// In opposition, a value of `1.0` means the emissive color is multiplied by the camera exposure.
///
/// Defaults to `0.0`
pub emissive_exposure_weight: f32,
/// The UV channel to use for the [`StandardMaterial::emissive_texture`].
///
/// Defaults to [`UvChannel::Uv0`].
@ -683,6 +690,7 @@ impl Default for StandardMaterial {
base_color_channel: UvChannel::Uv0,
base_color_texture: None,
emissive: Color::BLACK,
emissive_exposure_weight: 0.0,
emissive_channel: UvChannel::Uv0,
emissive_texture: None,
// Matches Blender's default roughness.
@ -964,9 +972,12 @@ impl AsBindGroupShaderType<StandardMaterialUniform> for StandardMaterial {
flags |= StandardMaterialFlags::ATTENUATION_ENABLED;
}
let mut emissive = LinearRgba::from(self.emissive).to_f32_array();
emissive[3] = self.emissive_exposure_weight;
StandardMaterialUniform {
base_color: LinearRgba::from(self.base_color).to_f32_array().into(),
emissive: LinearRgba::from(self.emissive).to_f32_array().into(),
emissive: emissive.into(),
roughness: self.perceptual_roughness,
metallic: self.metallic,
reflectance: self.reflectance,

View file

@ -178,7 +178,6 @@ fn pbr_input_from_standard_material(
pbr_input.material.alpha_cutoff = pbr_bindings::material.alpha_cutoff;
// emissive
// TODO use .a for exposure compensation in HDR
var emissive: vec4<f32> = pbr_bindings::material.emissive;
#ifdef VERTEX_UVS
if ((pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_EMISSIVE_TEXTURE_BIT) != 0u) {
@ -191,7 +190,7 @@ fn pbr_input_from_standard_material(
uv,
#endif
bias,
).rgb, 1.0);
).rgb, emissive.a);
}
#endif
pbr_input.material.emissive = emissive;

View file

@ -224,7 +224,6 @@ fn apply_pbr_lighting(
) -> vec4<f32> {
var output_color: vec4<f32> = in.material.base_color;
// TODO use .a for exposure compensation in HDR
let emissive = in.material.emissive;
// calculate non-linear roughness from linear perceptualRoughness
@ -564,6 +563,8 @@ fn apply_pbr_lighting(
emissive_light = emissive_light * (0.04 + (1.0 - 0.04) * pow(1.0 - clearcoat_NdotV, 5.0));
#endif
emissive_light = emissive_light * mix(1.0, view_bindings::view.exposure, emissive.a);
#ifdef STANDARD_MATERIAL_SPECULAR_TRANSMISSION
transmitted_light += transmission::specular_transmissive_light(in.world_position, in.frag_coord.xyz, view_z, in.N, in.V, F0, ior, thickness, perceptual_roughness, specular_transmissive_color, specular_transmitted_environment_light).rgb;
@ -585,7 +586,7 @@ fn apply_pbr_lighting(
// Total light
output_color = vec4<f32>(
view_bindings::view.exposure * (transmitted_light + direct_light + indirect_light + emissive_light),
(view_bindings::view.exposure * (transmitted_light + direct_light + indirect_light)) + emissive_light,
output_color.a
);

View file

@ -41,15 +41,15 @@ fn setup_scene(
));
let material_emissive1 = materials.add(StandardMaterial {
emissive: Color::linear_rgb(23000.0, 9000.0, 3000.0), // 4. Put something bright in a dark environment to see the effect
emissive: Color::linear_rgb(13.99, 5.32, 2.0), // 4. Put something bright in a dark environment to see the effect
..default()
});
let material_emissive2 = materials.add(StandardMaterial {
emissive: Color::linear_rgb(3000.0, 23000.0, 9000.0),
emissive: Color::linear_rgb(2.0, 13.99, 5.32),
..default()
});
let material_emissive3 = materials.add(StandardMaterial {
emissive: Color::linear_rgb(9000.0, 3000.0, 23000.0),
emissive: Color::linear_rgb(5.32, 2.0, 13.99),
..default()
});
let material_non_emissive = materials.add(StandardMaterial {

View file

@ -35,11 +35,6 @@ fn add_lightmaps_to_meshes(
) {
let exposure = 250.0;
for (entity, name, material) in meshes.iter() {
if &**name == "Light" {
materials.get_mut(material).unwrap().emissive = Color::Srgba(Srgba::WHITE * exposure);
continue;
}
if &**name == "large_box" {
materials.get_mut(material).unwrap().lightmap_exposure = exposure;
commands.entity(entity).insert(Lightmap {

View file

@ -79,12 +79,12 @@ fn setup(
let sphere_mesh_direction = meshes.add(Sphere::new(0.1).mesh().uv(32, 18));
let red_emissive = materials.add(StandardMaterial {
base_color: RED.into(),
emissive: Color::linear_rgba(100.0, 0.0, 0.0, 0.0),
emissive: Color::linear_rgba(1.0, 0.0, 0.0, 0.0),
..default()
});
let maroon_emissive = materials.add(StandardMaterial {
base_color: MAROON.into(),
emissive: Color::linear_rgba(50.0, 0.0, 0.0, 0.0),
emissive: Color::linear_rgba(0.369, 0.0, 0.0, 0.0),
..default()
});

View file

@ -137,8 +137,8 @@ fn setup(
));
// Candle Flame
let scaled_white = LinearRgba::from(ANTIQUE_WHITE) * 80.;
let scaled_orange = LinearRgba::from(ORANGE_RED) * 16.;
let scaled_white = LinearRgba::from(ANTIQUE_WHITE) * 20.;
let scaled_orange = LinearRgba::from(ORANGE_RED) * 4.;
let emissive = LinearRgba {
red: scaled_white.red + scaled_orange.red,
green: scaled_white.green + scaled_orange.green,

View file

@ -99,7 +99,7 @@ fn generate_bodies(
mesh: meshes.add(Sphere::new(1.0).mesh().ico(5).unwrap()),
material: materials.add(StandardMaterial {
base_color: ORANGE_RED.into(),
emissive: (LinearRgba::from(ORANGE_RED) * 18.).into(),
emissive: (LinearRgba::from(ORANGE_RED) * 2.).into(),
..default()
}),
..default()