Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Merged by Bors] - Send emissive color to uniform as linear instead of sRGB #7897

Closed
wants to merge 3 commits into from

Conversation

coreh
Copy link
Contributor

@coreh coreh commented Mar 4, 2023

This produces more accurate results for the EmissiveStrengthTest glTF test case.

(Requires manually setting the emission, for now)

Before: Screenshot 2023-03-04 at 18 21 25

After: Screenshot 2023-03-04 at 18 20 57

Tagging @JMS55 as a co-author, since this fix is based on their experiments with emission.

Objective

  • Have more accurate results for the EmissiveStrengthTest glTF test case.

Solution

  • Make sure we send the emissive color as linear instead of sRGB.

Changelog

  • Emission strength is now correctly interpreted by the StandardMaterial as linear instead of sRGB.

Migration Guide

  • If you have previously manually specified emissive values with Color::rgb() and would like to retain the old visual results, you must now use Color::rgb_linear() instead;
  • If you have previously manually specified emissive values with Color::rgb_linear() and would like to retain the old visual results, you'll need to apply a one-time gamma calculation to your channels manually to get the actual linear RGB value:
    • For channel values greater than 0.0031308, use (1.055 * value.powf(1.0 / 2.4)) - 0.055;
    • For channel values lower than or equal to 0.0031308, use value * 12.92;
  • Otherwise, the results should now be more consistent with other tools/engines.

This produces more accurate results for the `EmissiveStrengthTest` glTF test case.

(Requires manually setting the emission, for now)

Co-authored-by: JMS55 <[email protected]>
@coreh
Copy link
Contributor Author

coreh commented Mar 4, 2023

To replicate the results, download the test file from Khronos then apply this patch:

diff --git a/examples/tools/scene_viewer/main.rs b/examples/tools/scene_viewer/main.rs
index ee084e0af..ebfbb0937 100644
--- a/examples/tools/scene_viewer/main.rs
+++ b/examples/tools/scene_viewer/main.rs
@@ -6,6 +6,8 @@
 //! With no arguments it will load the `FlightHelmet` glTF model from the repository assets subdirectory.
 
 use bevy::{
+    core_pipeline::{bloom::BloomSettings, tonemapping::Tonemapping},
+    gltf::Gltf,
     math::Vec3A,
     prelude::*,
     render::primitives::{Aabb, Sphere},
@@ -78,8 +80,28 @@ fn setup_scene_after_load(
     mut scene_handle: ResMut<SceneHandle>,
     asset_server: Res<AssetServer>,
     meshes: Query<(&GlobalTransform, Option<&Aabb>), With<Handle<Mesh>>>,
+
+    gltf_assets: Res<Assets<Gltf>>,
+    mut materials: ResMut<Assets<StandardMaterial>>,
 ) {
     if scene_handle.is_loaded && !*setup {
+        let gltf = gltf_assets.get(&scene_handle.gltf_handle).unwrap();
+
+        let mh = gltf.named_materials.get("Emit1".into()).unwrap();
+        materials.get_mut(mh).unwrap().emissive = Color::rgb_linear(0.1, 0.5, 0.9);
+
+        let mh = gltf.named_materials.get("Emit2".into()).unwrap();
+        materials.get_mut(mh).unwrap().emissive = Color::rgb_linear(0.1, 0.5, 0.9) * 2.0;
+
+        let mh = gltf.named_materials.get("Emit4".into()).unwrap();
+        materials.get_mut(mh).unwrap().emissive = Color::rgb_linear(0.1, 0.5, 0.9) * 4.0;
+
+        let mh = gltf.named_materials.get("Emit8".into()).unwrap();
+        materials.get_mut(mh).unwrap().emissive = Color::rgb_linear(0.1, 0.5, 0.9) * 8.0;
+
+        let mh = gltf.named_materials.get("Emit16".into()).unwrap();
+        materials.get_mut(mh).unwrap().emissive = Color::rgb_linear(0.1, 0.5, 0.9) * 16.0;
+
         *setup = true;
         // Find an approximate bounding box of the scene from its meshes
         if meshes.iter().any(|(_, maybe_aabb)| maybe_aabb.is_none()) {
@@ -123,32 +145,35 @@ fn setup_scene_after_load(
                 )
                 .looking_at(Vec3::from(aabb.center), Vec3::Y),
                 camera: Camera {
-                    is_active: false,
+                    is_active: true,
+                    hdr: true,
                     ..default()
                 },
+                tonemapping: Tonemapping::BlenderFilmic,
                 ..default()
             },
-            EnvironmentMapLight {
-                diffuse_map: asset_server
-                    .load("assets/environment_maps/pisa_diffuse_rgb9e5_zstd.ktx2"),
-                specular_map: asset_server
-                    .load("assets/environment_maps/pisa_specular_rgb9e5_zstd.ktx2"),
-            },
+            BloomSettings::default(),
+            // EnvironmentMapLight {
+            //     diffuse_map: asset_server
+            //         .load("assets/environment_maps/pisa_diffuse_rgb9e5_zstd.ktx2"),
+            //     specular_map: asset_server
+            //         .load("assets/environment_maps/pisa_specular_rgb9e5_zstd.ktx2"),
+            // },
             camera_controller,
         ));
 
-        // Spawn a default light if the scene does not have one
-        if !scene_handle.has_light {
-            info!("Spawning a directional light");
-            commands.spawn(DirectionalLightBundle {
-                directional_light: DirectionalLight {
-                    shadows_enabled: false,
-                    ..default()
-                },
-                ..default()
-            });
-
-            scene_handle.has_light = true;
-        }
+        // // Spawn a default light if the scene does not have one
+        // if !scene_handle.has_light {
+        //     info!("Spawning a directional light");
+        //     commands.spawn(DirectionalLightBundle {
+        //         directional_light: DirectionalLight {
+        //             shadows_enabled: true,
+        //             ..default()
+        //         },
+        //         ..default()
+        //     });
+
+        //     scene_handle.has_light = true;
+        // }
     }
 }
diff --git a/examples/tools/scene_viewer/scene_viewer_plugin.rs b/examples/tools/scene_viewer/scene_viewer_plugin.rs
index 33277e6e5..897ed246d 100644
--- a/examples/tools/scene_viewer/scene_viewer_plugin.rs
+++ b/examples/tools/scene_viewer/scene_viewer_plugin.rs
@@ -12,7 +12,7 @@ use super::camera_controller_plugin::*;
 
 #[derive(Resource)]
 pub struct SceneHandle {
-    gltf_handle: Handle<Gltf>,
+    pub gltf_handle: Handle<Gltf>,
     scene_index: usize,
     #[cfg(feature = "animation")]
     animations: Vec<Handle<AnimationClip>>,

@JMS55 JMS55 added this to the 0.10 milestone Mar 4, 2023
Copy link
Contributor

@JMS55 JMS55 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs a migration guide/changelog, but code (the one line that there is) is good :)

@coreh
Copy link
Contributor Author

coreh commented Mar 4, 2023

Added the migration guide.

@JMS55
Copy link
Contributor

JMS55 commented Mar 4, 2023

Actually @coreh new change is needed: fix bloom example values, they're too bloom-y after this.

@coreh
Copy link
Contributor Author

coreh commented Mar 4, 2023

image

image

Let me fix that

Copy link
Contributor

@superdump superdump left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The gltf loader does the right thing by interpreting the emissive factor values from the gltf as linear. However, the plain .into() in this PR was doing a linear to non-linear conversion, which was then being used as linear. The PR is correct.

Calculated via:

(1.055 * value.powf(1.0 / 2.4)) - 0.055

Co-authored-by: JMS55 <[email protected]>
@coreh
Copy link
Contributor Author

coreh commented Mar 4, 2023

Updated the guide with math extracted from this function and updated the bloom example using the same math to get the old results back:

image

@coreh
Copy link
Contributor Author

coreh commented Mar 4, 2023

Fixed two more examples. There's no real way to fix iter_combinations without removing the use of the ORANGE_RED constant, or replacing it with something else. But it's not really a rendering-focused example, and if anything one could argue that it makes it look more like the actual color 😆

Before:
image

After:
image

I guess I can use ORANGE instead?

@JMS55
Copy link
Contributor

JMS55 commented Mar 4, 2023

What about the bloom_2d example?

@coreh coreh changed the title Send emissive color as linear instead of sRGB to uniform Send emissive color to uniform as linear instead of sRGB Mar 4, 2023
@coreh
Copy link
Contributor Author

coreh commented Mar 4, 2023

The bloom_2d example should be “safe” from this change, since it doesn't go through pbr_material.rs in any way.

Edit: Confirmed that it does look the same.

@alice-i-cecile alice-i-cecile added C-Bug An unexpected or incorrect behavior A-Rendering Drawing game state to the screen S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it labels Mar 4, 2023
@cart
Copy link
Member

cart commented Mar 4, 2023

bors r+

bors bot pushed a commit that referenced this pull request Mar 4, 2023
This produces more accurate results for the `EmissiveStrengthTest` glTF test case.

(Requires manually setting the emission, for now)

Before: <img width="1392" alt="Screenshot 2023-03-04 at 18 21 25" src="https://user-images.githubusercontent.com/418473/222929455-c7363d52-7133-4d4e-9d6a-562098f6bbe8.png">

After: <img width="1392" alt="Screenshot 2023-03-04 at 18 20 57" src="https://user-images.githubusercontent.com/418473/222929454-3ea20ecb-0773-4aad-978c-3832353b6871.png">

Tagging @JMS55 as a co-author, since this fix is based on their experiments with emission.

# Objective

- Have more accurate results for the `EmissiveStrengthTest` glTF test case.

## Solution

- Make sure we send the emissive color as linear instead of sRGB.

---

## Changelog

- Emission strength is now correctly interpreted by the `StandardMaterial` as linear instead of sRGB.

## Migration Guide

- If you have previously manually specified emissive values with `Color::rgb()` and would like to retain the old visual results, you must now use `Color::rgb_linear()` instead;
- If you have previously manually specified emissive values with `Color::rgb_linear()` and would like to retain the old visual results, you'll need to apply a one-time gamma calculation to your channels manually to get the _actual_ linear RGB value: 
  - For channel values greater than `0.0031308`, use `(1.055 * value.powf(1.0 / 2.4)) - 0.055`;
  - For channel values lower than or equal to `0.0031308`, use `value * 12.92`;
- Otherwise, the results should now be more consistent with other tools/engines.
@bors bors bot changed the title Send emissive color to uniform as linear instead of sRGB [Merged by Bors] - Send emissive color to uniform as linear instead of sRGB Mar 4, 2023
@bors bors bot closed this Mar 4, 2023
Shfty pushed a commit to shfty-rust/bevy that referenced this pull request Mar 19, 2023
…7897)

This produces more accurate results for the `EmissiveStrengthTest` glTF test case.

(Requires manually setting the emission, for now)

Before: <img width="1392" alt="Screenshot 2023-03-04 at 18 21 25" src="https://user-images.githubusercontent.com/418473/222929455-c7363d52-7133-4d4e-9d6a-562098f6bbe8.png">

After: <img width="1392" alt="Screenshot 2023-03-04 at 18 20 57" src="https://user-images.githubusercontent.com/418473/222929454-3ea20ecb-0773-4aad-978c-3832353b6871.png">

Tagging @JMS55 as a co-author, since this fix is based on their experiments with emission.

# Objective

- Have more accurate results for the `EmissiveStrengthTest` glTF test case.

## Solution

- Make sure we send the emissive color as linear instead of sRGB.

---

## Changelog

- Emission strength is now correctly interpreted by the `StandardMaterial` as linear instead of sRGB.

## Migration Guide

- If you have previously manually specified emissive values with `Color::rgb()` and would like to retain the old visual results, you must now use `Color::rgb_linear()` instead;
- If you have previously manually specified emissive values with `Color::rgb_linear()` and would like to retain the old visual results, you'll need to apply a one-time gamma calculation to your channels manually to get the _actual_ linear RGB value: 
  - For channel values greater than `0.0031308`, use `(1.055 * value.powf(1.0 / 2.4)) - 0.055`;
  - For channel values lower than or equal to `0.0031308`, use `value * 12.92`;
- Otherwise, the results should now be more consistent with other tools/engines.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-Rendering Drawing game state to the screen C-Bug An unexpected or incorrect behavior S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants