-
-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Description
A-Frame 0.8.0 brings in some upstream fixes to THREE.GLTFLoader that are technically correct, but potentially inconsistent with existing scenes and examples. Pre-0.8.0, gltf-model assumed all textures were in linear color space — material.color and material.src still make this same assumption today. That's incorrect — images are usually sRGB, and the glTF spec explicitly requires color and emissive textures to use sRGB.
Following best practice:
- Textures are converted
sRGB->linearat the beginning of the fragment shader if the image encoding is set to sRGB. - Renderer calculations are done in linear space.
- Renderer output is converted
linear->sRGBfor the screen ifrenderer.gammaOutput=true.
By default in A-Frame and three.js, renderer.gammaOutput is false and step (3) never happens. As a result, glTF models look darker than they should. Textures using material.src look mostly right, because we don't set the encoding to sRGB, even though the images usually are sRGB. So the colors are being treated as linear in lighting calculations, which practically isn't very noticeable.
User danilo in Slack created this PDF, which illustrates the problem nicely:
https://drive.google.com/file/d/1BRdCU9nDzUHOfDYYXdbAV1cMW60Md6uv/view
I don't have any quick answers on what we should do here... in short, I think the three.js defaults assume some familiarity with color spaces, and ideally we would abstract this away from our users, or at least provide some best practices as a reference for users who are going to care about matching a color palette precisely.
If we were to change our defaults, here's a straw proposal:
- By default, set
renderer="gammaOutput: true". - By default, set
srcandemissiveMapboth to sRGB encoding. Perhaps providesrcColorSpaceandemissiveColorSpaceto override this, but I think those properties would be an escape hatch to restore legacy behavior and not otherwise useful.
The one thing this doesn't fix is material.color values, which are assumed to be already linear in three.js, and don't have an encoding setting. So either (1) we could provide guidance about converting an sRGB hex code to linear, or (2) we would automatically do this in the material component.
I'm also not sure what this all means for vertex colors...
My vote for first step would be to add srcColorSpace and emissiveColorSpace in a PR, verify that it improves outcomes, and then enable them without changing any defaults. It would be nice to have feedback from someone more familiar with color spaces and/or rendering pipelines. 🙂