glTF 2.0 exporter uses wrong color space #102905

Closed
opened 2022-12-01 22:58:46 +01:00 by TS · 6 comments

System Information
system-info.txt

Blender Version
As per the 'About Blender' screen:
3.3.1
Date: 2022-10-04 18:35
Hash: b292cfe5a936
Branch: master

Short description of error
The glTF 2.0 spec mandates that colors be represented in the sRGB color space, but Blender currently erroneously exports colors in the scene linear color space, resulting in glTF 2.0 exports having an incorrect appearance when imported into other software.
This requirement is described in various parts of the spec, such as section 3.9 :

The texture MUST contain 8-bit values encoded with the sRGB opto-electronic transfer function...

And section 5.22 :

The first three components (RGB) MUST be encoded with the sRGB transfer function.

etc.

Here are a few example screenshots produced with the attached minimal Bevy application and .blend file, showing the differences in color between Blender renders and glTF 2.0 exports:

Blender: grey-blender.png Bevy: grey-bevy.png Blender: lime-blender.png Bevy: lime-bevy.png Blender: cyan-blender.png Bevy: cyan-bevy.png

These examples use an unlit material for simplicity, and to remove lighting as a factor that may affect the perceived color, but this issue also exists with most other ways that colors can be described in a glTF 2.0 export.

Exact steps for others to reproduce the error

  • Open the attached .blend file in Blender
  • Optionally change the color of the cube in the Shading tab if you wish
  • Render the scene to an image and observe the color of the cube
  • Begin exporting the scene to a .gltf file
  • Ensure that Cameras is checked under the Include options in the export menu
  • Export the scene to a file exactly named 'repro.gltf'
  • Decompress the attached archive of a minimal Bevy application
  • If you changed the color of the cube, replace the 'repro.gltf' file in the decompressed assets folder with your exported scene
  • Install Rust if you need to
  • Run the command 'cargo run' in the decompressed app's folder to compile and run the application

Observe the color of the cube as rendered in the application, and compare it to the color of the cube in the Blender render

repro.blend
repro-bevy.tar.gz

**System Information** [system-info.txt](https://archive.blender.org/developer/F13984594/system-info.txt) **Blender Version** As per the 'About Blender' screen: 3.3.1 Date: 2022-10-04 18:35 Hash: b292cfe5a936 Branch: master **Short description of error** The glTF 2.0 spec mandates that colors be represented in the sRGB color space, but Blender currently erroneously exports colors in the scene linear color space, resulting in glTF 2.0 exports having an incorrect appearance when imported into other software. This requirement is described in various parts of the spec, such as [section 3.9 ](https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#materials): > The texture MUST contain 8-bit values encoded with the sRGB opto-electronic transfer function... And [section 5.22 ](https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#reference-material-pbrmetallicroughness): > The first three components (RGB) MUST be encoded with the sRGB transfer function. etc. Here are a few example screenshots produced with the attached minimal Bevy application and .blend file, showing the differences in color between Blender renders and glTF 2.0 exports: | Blender: ![grey-blender.png](https://archive.blender.org/developer/F13984605/grey-blender.png) Bevy: ![grey-bevy.png](https://archive.blender.org/developer/F13984604/grey-bevy.png) | Blender: ![lime-blender.png](https://archive.blender.org/developer/F13984606/lime-blender.png) Bevy: ![lime-bevy.png](https://archive.blender.org/developer/F13984603/lime-bevy.png) | Blender: ![cyan-blender.png](https://archive.blender.org/developer/F13984607/cyan-blender.png) Bevy: ![cyan-bevy.png](https://archive.blender.org/developer/F13984602/cyan-bevy.png) | | -- | -- | -- | These examples use an unlit material for simplicity, and to remove lighting as a factor that may affect the perceived color, but this issue also exists with most other ways that colors can be described in a glTF 2.0 export. **Exact steps for others to reproduce the error** - Open the attached .blend file in Blender - Optionally change the color of the cube in the Shading tab if you wish - Render the scene to an image and observe the color of the cube - Begin exporting the scene to a .gltf file - Ensure that Cameras is checked under the Include options in the export menu - Export the scene to a file exactly named 'repro.gltf' - Decompress the attached archive of a minimal Bevy application - If you changed the color of the cube, replace the 'repro.gltf' file in the decompressed assets folder with your exported scene - [Install Rust ](https://www.rust-lang.org/) if you need to - Run the command 'cargo run' in the decompressed app's folder to compile and run the application # Observe the color of the cube as rendered in the application, and compare it to the color of the cube in the Blender render [repro.blend](https://archive.blender.org/developer/F13984633/repro.blend) [repro-bevy.tar.gz](https://archive.blender.org/developer/F13984645/repro-bevy.tar.gz)
Author

Added subscriber: @Pixelstorm

Added subscriber: @Pixelstorm

Added subscriber: @scurest

Added subscriber: @scurest

Actually in glTF, the space for color values in the JSON (plus vertex colors and most calculations) is linear space. Only the pixels in color textures (base color, emission) are in sRGB.

Expanding your quotes with more context, you can see both are actually talking about color textures

emissive : The emissive texture and factor control the color and intensity of the light being emitted by the material. The texture MUST contain 8-bit values encoded with the sRGB opto-electronic transfer function so RGB values MUST be decoded to real linear values before they are used for any computations.

material.pbrMetallicRoughness.baseColorTexture
The base color texture. The first three components (RGB) MUST be encoded with the sRGB transfer function.

See also this comment on the spec repo: https://github.com/KhronosGroup/glTF/issues/1609#issuecomment-491097601


Additionally when you compare colors between Blender and a viewer, you also need to change Blender's default Colorspace View Transform from "Filmic" to "Standard". Filmic applies a transform that essentially darkens all colors; standard passes the color through unchanged.

If I do that, then export and compare the unlit cube between Blender and https://gltf-viewer.donmccurdy.com/ I get the same color.

comp.png

Actually in glTF, the space for color values in the JSON (plus vertex colors and most calculations) is linear space. Only the pixels in *color textures* (base color, emission) are in sRGB. Expanding your quotes with more context, you can see both are actually talking about color textures > emissive : The emissive texture and factor control the color and intensity of the light being emitted by the material. **The texture MUST contain 8-bit values encoded with the sRGB opto-electronic transfer function** so RGB values MUST be decoded to real linear values before they are used for any computations. > material.pbrMetallicRoughness.baseColorTexture > The base color texture. **The first three components (RGB) MUST be encoded with the sRGB transfer function.** See also this comment on the spec repo: https://github.com/KhronosGroup/glTF/issues/1609#issuecomment-491097601 --- Additionally when you compare colors between Blender and a viewer, you also need to [change Blender's default Colorspace View Transform from "Filmic" to "Standard"](https://blender.stackexchange.com/questions/164677/images-as-emitters-constantly-come-out-dull-white-emission-not-actually-white). Filmic applies a transform that essentially darkens all colors; standard passes the color through unchanged. If I do that, then export and compare the unlit cube between Blender and https://gltf-viewer.donmccurdy.com/ I get the same color. ![comp.png](https://archive.blender.org/developer/F13984744/comp.png)
Member

Added subscriber: @JulienDuroure

Added subscriber: @JulienDuroure
Author

Changed status from 'Needs Triage' to: 'Resolved'

Changed status from 'Needs Triage' to: 'Resolved'
TS closed this issue 2022-12-02 23:39:55 +01:00
TS self-assigned this 2022-12-02 23:39:55 +01:00
Author

After looking into it some more, I've realized that yes, you're correct. I got it the wrong way round - the bug is actually in Bevy, which is interpreting colors in glTF files as sRGB instead of linear as it should be, and Blender is working perfectly fine.
I got correct, matching results after patching Bevy and following your advice about the view transform. (Although, why exactly does blender have a default setting that subtly changes all the colors to be different?)

I had to reread the glTF spec a few times to parse where it was specified that colors should be processed in linear - it's the phrase 'RGB values MUST be decoded to real linear values' from one of the aforementioned quotes. I think I (and the Bevy developers, evidently) got confused by the spec in this regard, because it always refers to color spaces as 'transfer functions' - a term that isn't defined in the spec's glossary, and that until now I had never heard before - so I didn't understand what the spec meant by 'linear transfer function', or what these 'real linear values' were meant to be.

After looking into it some more, I've realized that yes, you're correct. I got it the wrong way round - the bug is actually in Bevy, which is interpreting colors in glTF files as sRGB instead of linear as it should be, and Blender is working perfectly fine. I got correct, matching results after patching Bevy and following your advice about the view transform. (Although, why exactly does blender have a default setting that subtly changes all the colors to be different?) I had to reread the glTF spec a few times to parse where it was specified that colors should be processed in linear - it's the phrase 'RGB values MUST be decoded to real linear values' from one of the aforementioned quotes. I think I (and the Bevy developers, evidently) got confused by the spec in this regard, because it always refers to color spaces as 'transfer functions' - a term that isn't defined in the spec's glossary, and that until now I had never heard before - so I didn't understand what the spec meant by 'linear transfer function', or what these 'real linear values' were meant to be.
Sign in to join this conversation.
No Milestone
No project
No Assignees
3 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: blender/blender-addons#102905
No description provided.