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

Intensity units when exporting point lights #564

Closed
4DA opened this issue Jun 24, 2019 · 17 comments · Fixed by #1760
Closed

Intensity units when exporting point lights #564

4DA opened this issue Jun 24, 2019 · 17 comments · Fixed by #1760
Labels
bug Something isn't working exporter This involves or affects the export process Mesh_&_Object

Comments

@4DA
Copy link

4DA commented Jun 24, 2019

Blender uses watts as units for power of point light sources.
On the other hand, KHR_punctual_lights use lm/sr as light intensity units.

I can see that values from point light power settings are directly written to gltf file without watts -> lumen -> candela conversion.

@donmccurdy
Copy link
Contributor

/cc #24 and #528.

Previously we weren't sure what Blender's units were, but the UI does pretty clearly show watts now anyway...
Screen Shot 2019-06-24 at 9 50 49 AM

@4DA
Copy link
Author

4DA commented Jun 24, 2019

As far as I understand Power field in light settings denotes wattage emitted by light source, not consumed, otherwise everything may be even more difficult.

I've done some back-of-the-envolope computation, so here it goes:

- Kv (683) - maximum spectral luminous efficacy of radiation for photoscopic vision,
  for 555 nm wavelength, that is most perceptually efficient for human vision
- Φe - radiant flux [watts]
- Φv - luminous flux [lumens]
- Iv - luminous intensity [lm/sr or candela], what KHR_punctual_lights mandates for point lights
--
Φv = Kv * Φe
Iv = Φv / 4PI
=>
Lv = Kv * Φe / 4PI // for point light source

Computations are a bit subtler for spot lights:
https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf

I am not totally sure we should have this very watt -> lumen conversion, but at least it gets the physical units correct, the rest I guess should be handled by gltf model consumer.

@julienduroure julienduroure added exporter This involves or affects the export process Mesh_&_Object labels Jun 26, 2019
@aguaviva
Copy link

aguaviva commented Aug 1, 2019

Any chance to get this tagged as a bug? The units for the light intensity seems to be wrong.

According to the glTF specs:

  1. point and spot lights use luminous intensity in candela (lm/sr), this exporter uses Watts
  2. directional lights use illuminance in lux (lm/m2), this exporter uses "Strength"

the exported units should be as specified in the glTF specs.

@julienduroure julienduroure added the bug Something isn't working label Aug 1, 2019
@julienduroure
Copy link
Collaborator

Tagged as bug

@johndrmiller
Copy link

Not for nothing, but it took me about three hours of searching to find this thread so that I could find out how to convert watts to lumens because I'm primarily a designer with a limited math background. I'm currently trying to import a gltf into three.js that was exported from blender by another designer.

On one hand, thank you so much for posting those formulas. On the other, is there any idea when the fix for this might be implemented?

Thank you!

@toji
Copy link
Member

toji commented Apr 16, 2021

Raises hand ✋

Add one more developer to the list of "People who sunk multiple hours into figuring out the issue in their code only to learn it was a problem in Blender's export." It would be really great if this could be fixed!

@mindinsomnia
Copy link

How about we start by writing a function to do this conversion from Blender watts to the correct value for glTF for point lights, area lights and sun lights, then validate the conversion is correct, and if it is then it should be fairly trivial to just throw it into the code.

  • Kv (683) - maximum spectral luminous efficacy of radiation for photoscopic vision,
    for 555 nm wavelength, that is most perceptually efficient for human vision
  • Φe - radiant flux [watts]
  • Φv - luminous flux [lumens]
  • Iv - luminous intensity [lm/sr or candela], what KHR_punctual_lights mandates for point lights
    --
    Φv = Kv * Φe
    Iv = Φv / 4PI
    =>
    Lv = Kv * Φe / 4PI // for point light source

Based on that, it seems like the function would be roughly:

import math
# Convert Blender Point light watts units to Candela for glTF spec
def blender_watts_to_lumens(watt):
	return (683 * watt)  / ( 4 * math.pi)

I wrote a little test to output results and this is what I got from that function:

import math

def blender_watts_to_lumens(watt):
	return (683 * watt)  / ( 4 * math.pi )
	
values = [0.1, 0.2, 0.5, 1.0, 2, 5, 10, 20, 50, 100]

for i in values:
	print(str(i) + " Blender Watts = " + str(blender_watts_to_lumens(i)))
0.1 Blender Watts = 5.435141306588226
0.2 Blender Watts = 10.870282613176451
0.5 Blender Watts = 27.17570653294113
1.0 Blender Watts = 54.35141306588226
2 Blender Watts = 108.70282613176452
5 Blender Watts = 271.7570653294113
10 Blender Watts = 543.5141306588226
20 Blender Watts = 1087.0282613176453
50 Blender Watts = 2717.570653294113
100 Blender Watts = 5435.141306588226

@4DA Does that look correct in your opinion?

@donmccurdy
Copy link
Contributor

donmccurdy commented Jan 3, 2022

You can find the current export code here:

def __gather_intensity(blender_lamp, _) -> Optional[float]:
emission_node = __get_cycles_emission_node(blender_lamp)
if emission_node is not None:
if blender_lamp.type != 'SUN':
# When using cycles, the strength should be influenced by a LightFalloff node
result = gltf2_blender_search_node_tree.from_socket(
emission_node.inputs.get("Strength"),
gltf2_blender_search_node_tree.FilterByType(bpy.types.ShaderNodeLightFalloff)
)
if result:
quadratic_falloff_node = result[0].shader_node
emission_strength = quadratic_falloff_node.inputs["Strength"].default_value / (math.pi * 4.0)
else:
gltf2_io_debug.print_console('WARNING',
'No quadratic light falloff node attached to emission strength property')
emission_strength = blender_lamp.energy
else:
emission_strength = emission_node.inputs["Strength"].default_value
return emission_strength
return blender_lamp.energy

There are some edge cases around Cycles vs. Eevee and falloff that would probably require some manual testing here, I'm not sure this is quite as simple as just plugging in a watts-to-lumens conversion formula unfortunately.

@j-conrad
Copy link

Forgive me if this has already been discussed here or elsewhere, but it's worth noting Blender's manual that explains how the power of lights are calculated. It doesn't seem to be particularly standard with other software since it specifies:

  1. Direct (Sun) lights use Watts per square meter
  2. The other light types don't use typical 'electrical Watts'

The power of sun lights is specified in Watts per square meter. The power of point lights, spot lights, and area lights is specified in Watts. But this is not the electrical Watts that consumer light bulbs are rated at. It is Radiant Flux or Radiant Power which is also measured in Watts. It is the energy radiated from the light in the form of visible light.

If you want to set the power to real world values, you have to convert the wattage of consumer bulbs or LED lights to radiant flux, but it is not a straightforward process. The wattage of bulbs means the electrical power required to power them. LED lights have a “Watt equivalent” which is neither the electrical power they require nor the amount of light they put out. Some consumer lights specify lumens or luminous flux which is the radiant flux weighted with the wavelengths perceived by the human eye.

It goes on to list a couple handy conversion charts.
Blender Manual: The Power of Lights

@atteneder
Copy link
Contributor

atteneder commented Jun 13, 2022

@mindinsomnia Thanks for jumping ahead and providing the conversion functions.

Is it a good idea to pick the luminous efficacy of 555nm wavelength light (essentially a green laser) as conversion reference?

The luminous efficacy depends on the type of light (see examples). I'd argue most Blender users try to replicate the sun or consumer lights (LED, halogen or incandescent lights) whose luminous efficacy is a lot lower.

I'm not an expert, but it seems the most physically accurate way would be to defer the luminous efficacy off of the light's color, right? (No idea how to calculate that though; maybe blend 3 defined values based on RGB factors).

edit: Furthermore the luminous efficacy differs between day and night, but we need to draw the line somewhere and since I'm not aware of renderers that replicate human night vision, I'd say ignore that.

@atteneder
Copy link
Contributor

Also: For directional lights we need to convert W/m² to lux (i.e. lumen/m²), so no need to divide by the area of a sphere, right?

will-ca added a commit to will-ca/glTF-Blender-IO that referenced this issue Oct 12, 2022
will-ca added a commit to will-ca/glTF that referenced this issue Oct 12, 2022
Lumens are *not* actually a physical unit. They are a perceptual unit, and this leads to all sorts of issues.

- Lumens makes RGB difficult to process and reason about, because the same physical power of blue and red produces fewer perceptual lumens than does green.
- Lumens produce very big numbers, in the hundreds for a household lightbulb. This may be petty, but is a compatibility issue when viewers and their importers expect numbers around the 0.0-10.0 range.
- Lumens (perceptual luminance) are not actually convertible from watts (physical radiance), which are what authoring programs and rendering pipelines work in. This has two consequences:
- In logistical terms, it makes implementation of exporters and importers tricky. E.G. KhronosGroup/glTF-Blender-IO#564 was held up for over three years (and is still open) because nobody figured out the conversion function. Other projects, including Three.JS, and the official Khronos Group reference GLTF viewer, seem to also have just ignored the units part of the spec.
- In mathematical terms, it means that there is not actually a canonical way to convert from physical watts in authoring and display programs to perceptual lumens for GLTF, or vice versa. They measure different things, so at best you can have a wild guess based on situational equivalence given a bunch of arbitrary assumptions. E.G. 594lm, 609lm, 572lm, and 0.2lm are all equally valid conversions for 1W, depending on the exact wavelength and luminous efficiency function you're using. **This means that as written, the spec's behaviour is actually undefined relative to physically based workflows;** It is actually not possible to build a physically accurate pipeline using lumens as specified in `KHR_lights_punctual`, because the unit itself does not represent a physical quantity.

Due to low previous adoption of the lumens unit from the spec (possibly partly due to the aforementioned issues), the disruption from this should hopefully be minimial. I have not found any examples that actually implement or use the lumens units from the spec, including both the Three.JS and official Khronos Group reference GLTF viewers. In any case applications are free to maintain their present, already non-conformant behaviour, and adopt the new behaviour if they wish.

See KhronosGroupGH-2213.
@donmccurdy
Copy link
Contributor

Further discussion in KhronosGroup/glTF repository —

KhronosGroup/glTF#2214

The conversion does not seem to be correct with just a watts-to-lux formula, unfortunately.

@donmccurdy
Copy link
Contributor

FYI – @will-ca has opened a PR with a proposed fix in #1760, testing would be welcome! Important to note that when exporting with physically-based units (per the glTF spec) you will almost certainly need to adjust the exposure in your viewer.

The PR does include a second "compatibility" mode that exports a unitless light intensity value. That isn't spec-compliant or physically-based, but seems to be a common workflow and may work out of the box in viewers without exposure controls.

@DrewImm
Copy link

DrewImm commented Nov 27, 2023

Just downloaded blender the other day and this still seems to be an issue. Blender shipped with 4.0.44

Not sure why this is closed?

@donmccurdy
Copy link
Contributor

donmccurdy commented Nov 27, 2023

@DrewImm the unit conversions are implemented, with a few different options in the exporter settings. Note that getting your lighting to appear identical in an application outside Blender remains difficult - the application's exposure and tone mapping come into play, for one thing, and an exposure of "1" is not necessarily the same in one application as in another.

If you feel there's a bug in the Blender exporter, though, please feel free to file an issue – we'd need a .blend file to investigate.

@DrewImm
Copy link

DrewImm commented Nov 27, 2023

Thanks @donmccurdy!! I found the Lighting dropdown and tried Unitless, which seems way closer to what I'd expect. Is there documentation on the options?

@donmccurdy
Copy link
Contributor

Not more than is in the tooltips, at the moment. Short summary:

  • Raw: Matches previous Blender versions, but probably isn't useful for much beyond that.
  • Standard: Does exact exports of light intensities with physically-based units. This is the only "correct" option, but there is a catch.... having the correct units doesn't guarantee the same result in two different renderers. Likely you'll need to adjust the exposure in the application viewing the model.
  • Unitless: Exports lights with unitless values, relative to default exposure. If both Blender and the viewing application are using their default exposure, this is likely to give fairly close results, but it isn't technically correct as a conversion of units, or according to the glTF specification.

To get a perfect reproduction (or as close as you can get, given inherent differences in applications...) I think you would need to also transfer the exposure and tone mapping to the target application, and I don't have a simple answer on how to do that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working exporter This involves or affects the export process Mesh_&_Object
Projects
None yet
10 participants