godotengine / godot-proposals

Godot Improvement Proposals (GIPs)
MIT License
1.17k stars 98 forks source link

Implement support for physical light quantity (lumens/lux) #723

Closed clayjohn closed 2 years ago

clayjohn commented 4 years ago

Describe the project you are working on: The Godot engine. :)

Describe the problem or limitation you are having in your project: The renderer is currently using arbitrary light units and a user-specified light falloff curve together with an energy multiplier. While this is fine for many use-cases, it makes it very difficult for users to specify physically plausible light values, and it makes it nearly impossible to export a scene from Blender and match the light values.

Describe the feature / enhancement and how it helps to overcome the problem or limitation: I propose we add a method for switching between arbitrary units and physical units for lights. This could be in a ProjectSetting, or it could be a toggle within the light itself.

The ability to specify physical light values makes it significantly easier to light scenes realistically. For example, standard 45/60 watt household light bulbs produce about 1200 lumens. If the engine works in physical light quanities, users could create an OmniLight3D with 1200 lumens and an appropriate size and then expect it to light up a room-sized room accurately and in a physical plausible way. The current system involves a lot of tweaking and hacks and still may look wrong.

I further propose that we use inverse square falloff with a user tweakable linear falloff curve. Proper inverse square falloff is needed for lights to look realistic and the linear falloff is still needed because, for efficiency, game engines need to limit the distances that lights can project. Falloff could be specified with a Curve resource that is baked into a texture.

Note: other engines are moving towards using physical light units. Unreal and Unity HDRP use Lux and Candela/Lumens; Unreal, Unity HDRP, and Cryengine use inverse square falloff. Blender users inverse square falloff as well, but it uses watts instead of lumens.

Describe how your proposal will work, with code, pseudocode, mockups, and/or diagrams: This proposal would change the UI of Light3D derived nodes to allow switching between Godot units and physical units. DirectionalLight3Ds would use lux (intensity over a square meter of receiving surface), while SpotLight3D and PointLight3D would use lumens/candela (intensity of light source in all directions). If possible, we would just remap these values into Godot units and only use Godot units internally.

Further, this proposal would change the current attenuation parameters for SpotLight3D and OmniLight3D to a curve resource that allows finer artistic control over the falloff curve. We could either default to inverse square falloff, and then replace by the curve when the curve is specified, or we could apply the curve over the inverse square falloff. Internally, We can store an array of falloff curves as they will all be the same dimension (1024x1) and then index them in the shader, making them easy to use and fast to run

If this enhancement will not be used often, can it be worked around with a few lines of script?: It can't be worked around in script.

Is there a reason why this should be core and not an add-on in the asset library?: The conversion from lux/luminance to Godot units could be done in an editor-plugin, however, the falloff curve is embedded deep inside the scene shaders. In order to support both properly, we need to edit the engine itself.

User questions
  1. Would it be preferable to only use physcial light units and remove Godot units altogether?
  2. Do users prefer a ProjectSetting, or a setting on a per-light basis?
reduz commented 4 years ago

My views on this:

Answering questions:

reduz commented 4 years ago

Some more points: physical light computation can be complex for a lot of users that don't want to work with realistic 3D, and we should not force it (and probably not force it by default). As such, I suggest we have a "light_units" with two selectable modes in the project setting:

For Normalized it should be about the same we have now, where lights have a simple "energy" value that is just a multiplier, and the camera exposure multiplier is just a single number. For Physical we use lux/lumen/etc. and physical camera exposure settings. Both are available as property, but the editor only exposes the one in the current mode.

reduz commented 4 years ago

Additionally, some effects like Bokeh and Bloom I don't believe it's a good idea to do them in a physically correct way. We should expose them instead in the way they offer more artistic freedom, even if the result can be unrealistic.

VicentGJ commented 4 years ago

Some more points: physical light computation can be complex for a lot of users that don't want to work with realistic 3D, and we should not force it (and probably not force it by default). As such, I suggest we have a "light_units" with two selectable modes in the project setting:

  • Normalized
  • Physical

For Normalized it should be about the same we have now, where lights have a simple "energy" value that is just a multiplier, and the camera exposure multiplier is just a single number. For Physical we use lux/lumen/etc. and physical camera exposure settings. Both are available as property, but the editor only exposes the one in the current mode.

What of those light modes you suggest to be the default one?

reduz commented 4 years ago

@VicentGJ definitely normalized

Cykyrios commented 4 years ago

Some more points: physical light computation can be complex for a lot of users that don't want to work with realistic 3D, and we should not force it (and probably not force it by default). As such, I suggest we have a "light_units" with two selectable modes in the project setting:

* Normalized

* Physical

For Normalized it should be about the same we have now, where lights have a simple "energy" value that is just a multiplier, and the camera exposure multiplier is just a single number. For Physical we use lux/lumen/etc. and physical camera exposure settings. Both are available as property, but the editor only exposes the one in the current mode.

I agree with all the points you made, except normalized units being the default. The reason for that is that units are not displayed in the editor (one distance unit is supposed to correspond to one meter, although this is not displayed). That would mean units for lights would not be displayed either, and the only difference between physical and normalized would be the falloff and actual numbers used in the light parameters. From the user's point of view, they would just have to tweak the values to get the lighting they want, so I believe it would make more sense to have physical units as default (even if that means you would need a value of several thousands to match the current Energy = 1, sensible defaults for room scale would be needed).

rubenjavier commented 4 years ago

Love the idea. I would also choose the physical model by default and make it work with watts, this way the numbers are lower (45 - 120) and its more compatible with Blender... ideally you could also choose if you want lumens/lux or watts, for those that come from other engines or architecture, also in case Blender ever upgrade its settings and add lumens/lux, wich in OSS is very possible for someone to add it

1abinitio1 commented 4 years ago

When in the editor, you could add an option to display light units in Ev100. Unity did this so you can avoid having giant numbers when setting the light intensity. Not sure what Ev100 actually is, but in practice it works the same way as decibels. (the difference between 10-20 is much smaller that the difference 100-110)

DarkKilauea commented 4 years ago

I'd actually like to make a case for Physical being the default mode here.

When I'm lighting scenes, I usually like to start with looking up similar light sources on the internet, then using those values in my lights in my scene. This allows me to get a realistic look quickly, which I can then adjust to get the look I'm going for.

Right now, I can't really do that in Godot, leading me to blindly adjust "knobs" until I get the look I'm going for.

So, that answers why the feature should exist, why then should it be the default? For new game developers (like myself) it would be helpful to have units that we can reference to get a good result quickly, especially since we haven't acquired a good feel for the normalized values yet. It would also help developers from other game engines, like Unity and Unreal, to migrate over to Godot. Having new users have to know to go in and flip a switch in order to get the lights to work in a physical manner is one more barrier to trying out the engine that doesn't need to be there. It also lines up better with modeling programs, which seem to be switching over to a more physically correct model for lighting. Having the model match by default will reduce surprises when porting scenes into the engine.

For existing projects, the system should default to Normalized mode for backwards compatibility. Existing users can then migrate over their lights over time if they'd like.

lawnjelly commented 4 years ago

Kostas Anagnostou's blog post here is quite relevant, in terms of falloff and physical units and validating with mitsuba: https://interplayoflight.wordpress.com/2020/04/19/validating-physical-light-units/

I know next to nothing about all this, but here are some rambling thoughts:

Point Lights and Sphere Area

As I understand it, the inverse square law is based on how the area of a sphere increases with distance:

Area = 4 * PI * (radius * radius)

So if a given 'power' of light, the Intensity at a given distance is:

I = Power / Area = Power / (4 * PI * (radius * radius))

However, there are few things that jump out:

It might be sensible to standardise that maximum light intensity is occurring at zero distance from the light, and equals:

Power / 1.0

For this you'd equate radius 0.0 with being 0.282094791 i.e.

Area = 4 * PI * (0.282 * 0.282) = 1.0

You could add 0.282 to your game radius to achieve this.

Focused / Directional Lights

Now consider this relationship is for a very specific scenario: A point light. A point light is assuming that light rays are travelling out in all directions equally. However, for non point light sources, especially focused light (light a car headlight, torch or laser), this is not true.

If we consider the sun as an extreme example, and if we consider it as a point (rather than having an area):

In the same way if you consider a torch (flashlight) with focus adjusting:

All this can happen automatically in a ray tracer if you consider ray directions. However for approximations you need some other approach to take into account light focus.

An approximation

One easy approximation is this: If we consider that the light from the sun in the atmosphere of the earth approximates a directional light (like a focused torch or laser), the important factor is that the distance in terms of inverse square is a long way from the source, and the relative distance involved between e.g. the sky and the ground area are small.

We can achieve the same kind of relationship (varying the focus), simply by varying the earlier offset we used to apply to the radius.

i.e. instead of saying our area 1.0 occurs at:

Area = 4 * PI * (0.282 * 0.282) = 1.0

We might say our 1.0 occurs at

Area = 0.01 * (10.0 * 10.0) = 1.0

I.e. at radius zero from the light source we add 10.0 to the radius, plug into the formula above and get full intensity at zero distance. At distance 1.0 from the light source add add 10.0 to the radius, plug into the formula and we get:

Area = 0.01 * (11 * 11) = 1.21
Intensity = Power / 1.21

Thus the intensity drops at a lower rate, and we can vary between a point light and a directional light by changing both the radius offsets and the multiplier, but still guarantee that we will have full 'power' at distance 0.

This is all probably horribly wrong physically but I'm just trying to think of things that might work in a game scenario and be fast and practical. I also have done absolutely zero reading around this so there might be a much better way.

The other cool thing about this kind of technique of focused light is that you can apply it to light profiles mentioned here #715 (e.g. from a lamp shade) and use the intensity in any given direction to also approximately affect the light focus (e.g. the lighter areas can be considered more focused), and get nice volumetric variation in lighting.

TokisanGames commented 4 years ago

If physical lights were available, I'd only use them. This is like asking if you want to use the older blender internal renderer or cycles? No question about it.

As for units, watts should NOT be used. Electrical power output is not a measure of light output. Lightbulbs provide power consumption, but Blender uses "power output as light" so its not even the same as Lightbulbs. 100 watts through an incandescent, CFL, or LED lights are an order of magnitude different.

Lumens is the unit that should be used for light output. Every physical bulb sold should have this rating and it describes the perceived light output of the light. Lumens refers to photon output. Lux refers to how many lumens hit an object, so it embeds falloff. Since falloff is adjustable, lux is not a good measurement.

With physical lights I should eventually be able to save or have presets so I can define a medium hard shadow, faster falloff LED at 5600 Kelvin, a hard shadow, slow falloff 3200k incandescent, or a softer shadow, slower falloff, 4100k fluorescent.

Here's a really good overview of physically based lights in blender, and describes some important concepts and even has lumen conversion for blender's light wattage output: https://youtu.be/ipqyVWm5JmY

Edit: Re: bokeh or bloom, artistic freedom is better than mimicking the physical reality of random lenses.

Re: per light setting, I can't imagine why one would want to mix physically based lights and digital fake lights. That's like preparing a fine meal with expensive sauces, then adding ketchup.

rubenjavier commented 4 years ago

@tinmanjuggernaut I didnt knew that about the watts in blender lights, in that case I change mi vote, I think it should be lumens the default physical units. off topic but maybe also add a request on blender to add regular lumens as a unit for lights, better for standards and for a blender to godot exporter

TokisanGames commented 4 years ago

@rubenjavier in the video I posted Jonathan talked about a couple plugins that allow you to insert lumens directly in blender, and displays the formula conversion for blender watts to lumens, so a Godot importer could translate directly without using blender's strange units.

mindinsomnia commented 4 years ago

Completely support this proposal, it would be a fantastic improvement.

Calinou commented 4 years ago

@mindinsomnia Please don't bump issues without contributing significant new information. Use the :+1: reaction button on the first post instead.

Calinou commented 3 years ago

Physical light falloff was implemented in https://github.com/godotengine/godot/pull/44941. Physical light quantity remains to be implemented, but it will probably be added to a future 4.x release rather than 4.0.

timshannon commented 3 years ago

FWIW, it looks like blender's GLTF exporter currently isn't doing the watts -> lumens conversion correctly (or at all?):

https://github.com/KhronosGroup/glTF-Blender-IO/issues/564

atirut-w commented 3 years ago

Physical light quantity remains to be implemented

Why couldn't it be implemented alongside the physical falloff?

Calinou commented 3 years ago

Why couldn't it be implemented alongside the physical falloff?

Physical light quantity is an entirely different concept from physical light falloff. They can (and should) be implemented independently from each other.

Calinou commented 2 years ago

Closing in favor of https://github.com/godotengine/godot-proposals/issues/4257, which is more up-to-date on the current plans.

Edit: This proposal is now implemented in 4.0: https://github.com/godotengine/godot/pull/63751