Skip to content

Commit

Permalink
Add emissive_exposure_weight to the StandardMaterial (bevyengine#13350)
Browse files Browse the repository at this point in the history
# Objective

- The emissive color gets multiplied by the camera exposure value. But
this cancels out almost any emissive effect.
- Fixes bevyengine#13133
- Closes PR bevyengine#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

https://github.com/google/filament/blob/4f021583f1c721486baaa9291be5943216c244ec/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)
  • Loading branch information
geckoxx committed May 17, 2024
1 parent 47d6e96 commit 1fcf6a4
Show file tree
Hide file tree
Showing 8 changed files with 24 additions and 18 deletions.
13 changes: 12 additions & 1 deletion crates/bevy_pbr/src/pbr_material.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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`].
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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,
Expand Down
3 changes: 1 addition & 2 deletions crates/bevy_pbr/src/render/pbr_fragment.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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;
Expand Down
5 changes: 3 additions & 2 deletions crates/bevy_pbr/src/render/pbr_functions.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;

Expand All @@ -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
);

Expand Down
6 changes: 3 additions & 3 deletions examples/3d/bloom_3d.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
5 changes: 0 additions & 5 deletions examples/3d/lightmaps.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
4 changes: 2 additions & 2 deletions examples/3d/spotlight.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
});

Expand Down
4 changes: 2 additions & 2 deletions examples/3d/transmission.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion examples/ecs/iter_combinations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down

0 comments on commit 1fcf6a4

Please sign in to comment.