KhronosGroup / glTF-Blender-IO

Blender glTF 2.0 importer and exporter
https://docs.blender.org/manual/en/latest/addons/import_export/scene_gltf2.html
Apache License 2.0
1.49k stars 319 forks source link

Emissive strength probably needs unit conversion. #1766

Open will-ca opened 2 years ago

will-ca commented 2 years ago

I'm probably going to leave this for now, but FYI similarly to #1760, emissivity strengths will almost certainly also need to be adjusted to be spec compliant.


TL;DR What needs to be done:

Make sure KHR_materials_emissive_strength is being used.

That should look right. If it doesn't (and it may not), then everything below will need to be checked:


Target unit is cd/m^2 (nits). https://gltf-transform.donmccurdy.com/classes/extensions.materialsemissivestrength.html

Output should be something like the multiplication of Strength with... Actually, just strength, I guess, since Factor and Texture can map directly to the exported file... Or leave Strength the same but convert Factor? Either way: All three of Factor, Texture, and Strength need to be included, and a conversion coefficient needs to be applied exactly once to exactly one of them. https://github.com/KhronosGroup/gltf/tree/main/extensions/2.0/Khronos/KHR_materials_emissive_strength#physical-units https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#additional-textures

Blender uses W/m^2. https://docs.blender.org/manual/en/latest/render/shader_nodes/shader/emission.html

If it is necessary to experimentally validate the unit on the Blender side, the technique I describe in this StackExchange answer and apply in this Github comment can be used: https://blender.stackexchange.com/a/222963/79413 https://github.com/KhronosGroup/glTF/pull/2214#issuecomment-1278292085

With linear colour space and no other light sources (including ambient), confirm C.2 == C.1/A.1 * (E.2/E.1) to verify that surface emission strength in Blender is in W/m^2.

At that point, the conversion formula from the numeric values in Blender to numeric values that are glTF spec-compliant should be something like f(e)=e*683/(2π). (Note: Online sources for converting lux to nits (which is a substep of this) seem to suggest using π instead of . I have no idea why they say that. The derivation should be (W/hemisphere/m**2) * (683lm/W) * (hemisphere/(2pi*sr)). Play around I guess; GNU Units and Wolfram Alpha may help.)

The Watts-to-lumens factor of 683 is a de facto industry/physical/scientific standard. It is based on the typical peak luminous efficiency for human vision, with coherent light at 555nm, and it also shows up in the modern (2019) SI definition of the "candela" base unit. It is also used in #1760 for import and export of punctual lights. https://github.com/KhronosGroup/glTF/pull/2214#issuecomment-1276762979 https://en.wikipedia.org/wiki/Luminous_efficacy#Explanation https://en.wikipedia.org/wiki/Luminous_efficiency_function#Details https://en.wikipedia.org/wiki/Candela#Definition

Note that currently Three.JS, Babylon.JS, and the Khronos Viewer do not have exposure settings that display spec-compliant, physically-unitted luminances correctly by default. They operate on a unitless scale normalized to 1.0 for direct display output. This means that any GLTF files with lighting strengths specified in spec-compliant units will necessarily look extremely bright (Around 683 times brighter than they should). https://github.com/KhronosGroup/glTF/pull/2214 https://github.com/KhronosGroup/glTF/pull/2128

I believe that means that primarily three options are available for testing export results:

With any of these three techniques, exported files in the viewer should appear basically the same as the source file in Blender.

Some kind of "compatibility mode" option will likely be included in #1760 to support unitless workflows by simply disabling the 683 lumens-to-watts coefficient. That should probably also apply to emissivity conversions. IDK if it might also be worth moving "683" to a global constant somewhere. Done. It's in gltf2_blender_conversions.


Uh.... It's really not that complicated.. In theory, but in practice I suppose there is just a lot that needs to be verified and double-checked. The good news is that we already went through most of it for punctual lights. So if the results look right on the first try with an algebraically and physically valid conversion formula (I.E. Just multiply by 683/(2π) on export, and divide on import), then that should be good to go. If not... Then your guess is as good as mine how deep the rabbit hole will go. 🤷

I may need this sooner or later, so if it hasn't been implemented yet by then I suppose I'll take another look at it at that point. Otherwise.... Good luck to whoever decides to tackle this, I guess. 😅 May your unit conversions be stable and your colour mappings standardized.

julienduroure commented 2 years ago

Some additional note: This is probably not only for KHR_materials_emissive_strength (this extension is used only when factor > 1.0), but also for emissive factor (used when factor < 1.0)

donmccurdy commented 2 years ago

Note that currently Three.JS, Babylon.JS, and the Khronos Viewer do not have exposure settings that display spec-compliant, physically-unitted luminances correctly by default.

The babylon.js and Khronos viewers do have (presumably correct) exposure options, but those sliders are not prominent in the UI. I'll add something similar to my three.js viewer soon. Auto exposure is certainly harder, so the issue remains that any scene exported with physically-based light units will probably need a manual exposure adjustment in viewers. Agreed that testing in Filament is likely to be a good choice.

I agree that supporting similar physical vs. unitless modes (as in https://github.com/KhronosGroup/glTF-Blender-IO/pull/1760) is likely to be useful for emissive.