From 1fcf6a444f20af96ffa8ef72263f98736e93af51 Mon Sep 17 00:00:00 2001 From: Johannes Hackel Date: Fri, 17 May 2024 15:49:53 +0200 Subject: [PATCH] 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 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) --- crates/bevy_pbr/src/pbr_material.rs | 13 ++++++++++++- crates/bevy_pbr/src/render/pbr_fragment.wgsl | 3 +-- crates/bevy_pbr/src/render/pbr_functions.wgsl | 5 +++-- examples/3d/bloom_3d.rs | 6 +++--- examples/3d/lightmaps.rs | 5 ----- examples/3d/spotlight.rs | 4 ++-- examples/3d/transmission.rs | 4 ++-- examples/ecs/iter_combinations.rs | 2 +- 8 files changed, 24 insertions(+), 18 deletions(-) diff --git a/crates/bevy_pbr/src/pbr_material.rs b/crates/bevy_pbr/src/pbr_material.rs index 05d01e3902dd0..91cb1d21a207e 100644 --- a/crates/bevy_pbr/src/pbr_material.rs +++ b/crates/bevy_pbr/src/pbr_material.rs @@ -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 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, diff --git a/crates/bevy_pbr/src/render/pbr_fragment.wgsl b/crates/bevy_pbr/src/render/pbr_fragment.wgsl index b2bb252f871f6..52b299f4c9872 100644 --- a/crates/bevy_pbr/src/render/pbr_fragment.wgsl +++ b/crates/bevy_pbr/src/render/pbr_fragment.wgsl @@ -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 = 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; diff --git a/crates/bevy_pbr/src/render/pbr_functions.wgsl b/crates/bevy_pbr/src/render/pbr_functions.wgsl index f816c604360e4..f789b3f76a4aa 100644 --- a/crates/bevy_pbr/src/render/pbr_functions.wgsl +++ b/crates/bevy_pbr/src/render/pbr_functions.wgsl @@ -224,7 +224,6 @@ fn apply_pbr_lighting( ) -> vec4 { var output_color: vec4 = 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( - 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 ); diff --git a/examples/3d/bloom_3d.rs b/examples/3d/bloom_3d.rs index b2fd467942997..36d178491be62 100644 --- a/examples/3d/bloom_3d.rs +++ b/examples/3d/bloom_3d.rs @@ -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 { diff --git a/examples/3d/lightmaps.rs b/examples/3d/lightmaps.rs index fe7f2a8c59f2a..aeeb753f4d163 100644 --- a/examples/3d/lightmaps.rs +++ b/examples/3d/lightmaps.rs @@ -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 { diff --git a/examples/3d/spotlight.rs b/examples/3d/spotlight.rs index cee0a6de69a1b..b05fc9ff2f566 100644 --- a/examples/3d/spotlight.rs +++ b/examples/3d/spotlight.rs @@ -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() }); diff --git a/examples/3d/transmission.rs b/examples/3d/transmission.rs index b5fe128f9fe93..0fea6d0cf200d 100644 --- a/examples/3d/transmission.rs +++ b/examples/3d/transmission.rs @@ -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, diff --git a/examples/ecs/iter_combinations.rs b/examples/ecs/iter_combinations.rs index 698d2961c31cd..cb946bfc79a64 100644 --- a/examples/ecs/iter_combinations.rs +++ b/examples/ecs/iter_combinations.rs @@ -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()