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

Add support for physical light units #4257

Closed
reduz opened this issue Mar 22, 2022 · 19 comments · Fixed by godotengine/godot#63751
Closed

Add support for physical light units #4257

reduz opened this issue Mar 22, 2022 · 19 comments · Fixed by godotengine/godot#63751
Assignees
Milestone

Comments

@reduz
Copy link
Member

reduz commented Mar 22, 2022

Describe the project you are working on

Godot

Describe the problem or limitation you are having in your project

The new renderer in Godot 4 is for the most part complete (save pending reorganization) but one issue that misses to be tackled is physical light unit support.

The main issue with it is that it has to be implemented in a way that also allows Godot users to not use those if they don't desire, as many games do not really care about these, nor are looking towards photorealistic artistic styles.

Describe the feature / enhancement and how it helps to overcome the problem or limitation

The idea is relatively simple, right now Godot4 has the CameraEffects class. This class will be renamed to CameraLens.
This will be an abstract class that will allow for different implementations (physical or not), but also simply disabling physical light units altogether.

Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams

CameraEffects will be renamed to CameraLens. This will be an abstract class that will be inherited by two classes:

  • CameraLensPhysical
  • CameraLensPractical

It will be possible to set this resource to:

  • Environment (to make it default for all cameras)
  • Camera (to set or override environment)
  • BakedLightmap (to know which exposure to bake at)
  • VoxelGI (to know which exposure to bake at)

CameraLensPhysical

This version of CameraLens will contain the following properties:

  • frustum/FocalLength (in millimeters)
  • frustum/near
  • frustum/far
  • frustum/focus_distance (used for depth of field)
  • exposure/aperture (in f-stops) controls exposure and depth of field
  • exposure/shutter_speed (in seconds) controls exposure and motion blur
  • *exposure/multiplier when PLU is disabled this is the only exposed parameter
  • exposure/sensitivity/mode (MANUAL | AUTOMATIC)
  • exposure/sensitivity/amount (in ISO) controls exposure directly, visible in MANUAL mode
  • exposure/sensitivity/min_amount (in ISO) minimum amount (visible in AUTOMATIC mode)
  • exposure/sensitivity/max_amount (in ISO) maximum amount (visible in AUTOMATIC mode)
  • exposure/sensitivity/adjust_speed in interpolation units/second (visible in AUTOMATIC mode)

CameraLensPractical

This version of CameraLens will contain the following properties:

  • frustum/fov (in degrees)
  • frustum/fov_aspect (VERTICAL_HORIZONTAL)
  • frustum/near
  • frustum/far
  • exposure/mode (MANUAL | AUTOMATIC)
  • exposure/multiplier when PLU is disabled this is the only exposed parameter
  • exposure/amount (in ISO) controls exposure directly, visible in MANUAL mode
  • exposure/min_amount (in ISO) minimum amount (visible in AUTOMATIC mode)
  • exposure/max_amount (in ISO) maximum amount (visible in AUTOMATIC mode)
  • exposure/adjust_speed in interpolation units/second (visible in AUTOMATIC mode)
  • dof_far/enabled
  • dof_far/distance
  • dof_far/fade
  • dof_near/enabled
  • dof_near/distance
  • dof_near/fade
  • motion_blur/ms

Lights

DirectionalLight will have an extra property:

  • illuminance/lux (in lux, lumen/m^2) hidden in physical light units disabled
  • illuminance/multiplier

Omni/Spot Lights will have an extra property:

  • illuminance/lumen (in lumen)
  • illuminance/multiplier (in lumen)

Temperature might be supported, would be interesting to ask what you think.

Materials, Skybox, Etc.

Currently a single (energy) multiplier is used. I believe we should rename energy to:
energy/iso (in ev100, will be hidden in physical units are not enabled)
energy/multiplier (as just a multiplier)

If this enhancement will not be used often, can it be worked around with a few lines of script?

N/A

Is there a reason why this should be core and not an add-on in the asset library?

N/A

@Calinou Calinou changed the title Physicsal Light Units Support Add support for physicsal light units Mar 22, 2022
@Calinou
Copy link
Member

Calinou commented Mar 22, 2022

This version of CameraLens will contain the following properties:

  • frustum/FocalLength (in millimeters)

Isn't FOV aspect still needed for a physical camera? The camera needs to know in which direction FOV should be stretched as the aspect ratio changes. While defaulting to Hor+ (like Camera3D currently does) is a good thing, games in portrait mode will generally want to use Vert- FOV scaling.

Temperature might be supported, would be interesting to ask what you think.

I think temperature could be supported as a multiplier of the existing light color (which is white by default). This temperature property would be hidden when physical light units are disabled.

There should be a formula out there for converting a temperature value in Kelvin to a color value.

@darksylinc
Copy link

darksylinc commented Mar 22, 2022

One tiny implementation detail, suggestion: HDR is often done with RGBA16_FLOAT render targets but when working with physical units you can easily fall out of range (e.g. the sun typically has > 100.000 lux which is above HALF_FLT_MAX) and even while you're in range you can see banding.

In Ogre-Next we simply avoid that problem by dividing by 1024 when doing most calculations, and multiplying by 1024 when the true physical units are needed (i.e. that's usually when doing non-linear math like log / exp operations).
This keeps the range somewhere between [0; 100] (approx) which is enough precision for half16.

Increasing precision to RGBA32_FLOAT would be a massive waste of bandwidth.

Of course, this also means shaders that want to store light info after the multiplication must be highp.

@Calinou
Copy link
Member

Calinou commented Mar 22, 2022

and even while you're in range you can see banding.

Godot has a built-in debanding filter which works well in my experience. It could be enabled automatically when physical light units are used.

@darksylinc
Copy link

darksylinc commented Mar 22, 2022

Godot has a built-in debanding filter which works well in my experience.

Once you're approaching HALF_FLOAT_MAX (65519) no debanding will help you because the error is gigantic.

And after surpassing HALF_FLOAT_MAX (which you will, with low roughness specular highlights) you can end up with INFs. Many operations will turn into NaNs once INFs are involved.

A debanding filter won't be the solution.

@Calinou Calinou added this to the 4.0 milestone Mar 22, 2022
@Ansraer
Copy link

Ansraer commented Mar 22, 2022

exposure/min_amount (in ISO) minimum amount (visible in AUTOMATIC mode)
exposure/max_amount (in ISO) maximum amount (visible in AUTOMATIC mode)

Huh, I never even considered setting max and min values to control the auto exposure.
If we decide to add max/min amounts those should probably be optional, or set to such a large/small default value that they don't really matter. Cause I can totally see users opening issues cause their auto exposure doesn't work in too dark/bright scenes.

To better control how the auto exposure result looks I really think an EC curve should be part of your proposal. Thats also what most implementations I have seen do: Use a histogram to produce a mostly neutral image and then apply an exposure compensation curve (fed with the original frame lumination) to gently nudge it back towards the original illumination. This can be very useful to make sure that darker areas still look dark-ish, while actually being visible for players.

DirectionalLight will have an extra property
Omni/Spot Lights will have an extra property

Are you sure this should be an extra property? imo this should simply replace the current intensity value.

illuminance/lux (in lux, lumen/m^2) hidden if physical light units disabled

Why exactly would we want to make physical light units hideable? There is really no benefit to the current system when compared to physical light units. Even without fully understanding what the physical units mean artists could just play around with the value until they get what they are looking for, which is the same workflow they are currently using.

@reduz
Copy link
Member Author

reduz commented Mar 22, 2022

@Ansraer you need min and max for both numerical precision reasons and because both eyes and camera sensors have limits.

Regarding comparison to the current system. You are wrong regarding not being advantages. If light is multiplied by 1, you know the maximum amount of light is white so it jusy modulates the texture color. This is very useful for artistic purposes in many art styles. Also on GLES3 PLU will most likely not be supported.

Agree about using histogram but thats an implememtation detail.

@reduz
Copy link
Member Author

reduz commented Mar 22, 2022

@darksylinc That is interesting, thanks! I wonder how it compares to Filamemts approach of multiplying the lights by 1/exposure.

@Ansraer
Copy link

Ansraer commented Mar 22, 2022

you need min and max for both numerical precision reasons and because both eyes and camera sensors have limits.

true enough. I was more focused on exposing them to the user. But I suppose it can't hurt.

I think temperature could be supported as a multiplier of the existing light color (which is white by default). This temperature property would be hidden when physical light units are disabled.
There should be a formula out there for converting a temperature value in Kelvin to a color value.

Afaik the color temperature is only used to determine the chromaticity (not intensity) of the light. So I expected it to be a button next to the color field, that simply opens an alternative color picker.
And I don't see a reason to disable this for non-physical light units lights, since this only changes the color itself.
I threw together a quick mockup. Just imagine that there is an awesome thermostat symbol on the new button I added:

Totally awesome mockup that was definitely NOT created in paint

image

@darksylinc
Copy link

Huh, I never even considered setting max and min values to control the auto exposure.
If we decide to add max/min amounts those should probably be optional, or set to such a large/small default value that they don't really matter.

You will want those min/max values. You really do.

Unless the GI is near perfect in terms of accuracy, you'll quickly run into bursts of exposure spikes (because areas under shadow / indoor are greatly different from fully lit areas).

Additionally it creates the problem that if you look at a place that is very dark and occupies most of the screen; the auto exposure will try to compensate and blacks become white. Artists will ask "why is this black object white?". Answer: because the max exposure is too high.

This happens in real life too; our brains know not to adapt the eye if we're still staying outside but trying to look at the inside of an open door. You'd have to stare to that for a long time before the eye decides to adapt despite being outdoors.

But this problem happens a lot with a camera which is why you have to tap in the bright parts to tell the camera how to adjust the exposure (if it's a consumer-friendly point and shoot camera), or use pro mode where the camera will stick to ISO values which contain min and max exposures.

@Polyrhythm
Copy link

Polyrhythm commented Mar 22, 2022

Omni/Spot Lights will have an extra property:

illuminance/lumen (in lumen)
illuminance/multiplier (in lumen)

I'm not sure what part of this to correct first...

First off, it doesn't make any sense to measure the intensity of a point light source (area/spot/omni) in illuminance. Why? Because illuminance is measured off of a surface the light is hitting, which means that unless you standardize how far that surface is from the light, it's a meaningless measurement. The reason illuminance works for a directional light is because directional lights have no attenuation, so all surfaces receive the same illuminance value regardless of their position.

Second, lumens are not a measurement of illuminance, they're a measure of luminance (technically "luminous flux", meaning total perceived power of a light).

Third, it doesn't make sense to arbitrarily pick lumens over, say, candelas - and in fact, you can easily convert between the two.
1 cd = 4pi lm

Here's how I would refactor the whole thing:

Directional lights
Illuminance, lux

Spot or omni lights
Luminous flux, lumen or candela

Area light
Luminance lm or cd/m2 (if you don't normalize this, the intensity will change based on the size of the light)

Emissives
Luminance, cd/m2 or nit (same thing)

Light meter
For measuring all this crap. Spot meter mode that measures luminance (cd/m2 or nit) and EV of any surface.
Illuminance meter mode that reads lux off of any surface (valid only if materials are turned pure white).

There's probably stuff I'm forgetting, but that's a good start.

@reduz reduz changed the title Add support for physicsal light units Add support for physical light units Mar 22, 2022
@HaSa1002
Copy link

HaSa1002 commented Mar 24, 2022

Temperature might be supported, would be interesting to ask what you think.

I would strongly argue temperature colour editing being a must-have feature. It easily ensures your lights (even in non-realistisc art styles!) look correct. I missed this feature so much, I made an addon a couple months back: https://github.com/HaSa1002/godot-light-tools.
This looks like this and uses meta properties under the hood:

@TokisanGames
Copy link

All the points brought up in #723 still apply, but it seems some were skipped over. Including points about importing lights from blender. My notes here are long, so I'll break them up into separate camera and light sections.

To have an effective physical photography system, we should have enough parameters to correspond to real lights and cameras that filmmakers, photographers, and equipment use, plus the ability to save, import, and export presets for lights and cameras. You don't have to define the presets now, just have the ability, and others can put them in.

Cameras

Camera settings:

  • Sensor height and width dimensions
  • Lens focal length - you have this, but it will look different depending on the sensor size and shape
  • optional: blades to adjust different bokeh looks - or a separate post effect is fine
  • Zebra pattern for debugging +

Sensor preset examples:

  • 35mm Full frame (36 mm × 24 mm)
  • Canon ASP-C 1.6x cropped
  • Red Epic M-X 5K 27.7 mm x 14.6 mm
  • Arri ALEXA / AMIRA 16×9 23.8 x 13.4mm
  • Medium and Large format (6x6cm, 6x9cm, 4"x5", 5"x8")

+A cool feature in digital cameras is zebra mode. Turning this on points out areas in the viewport where the highlights are over-exposed. The hot areas are covered in a moving zebra stripe.

image

Think of a cinematographer using it how a game dev would use visual collision debugging. There might be problems you don't notice during development, so the computer can point out the issues that might become more prominent on other's systems.

This would be very useful when creating cut scenes, or setting up environment lighting and noticing lights gets too hot in a certain area.

@Polyrhythm
Copy link

The zebra is a cool idea. I think an improvement on that (or alternative) is false color, which will show you over-exposure, under-exposure, middle-grey (if exposing with a colorchecker or gray patch visible), and, ideally, user-definable zones.

image

The issue with either zebra or false color is that it will depend on the tonemapper used. The linear render buffer is going to have a larger dynamic range than the output display, so the min/max values for all this stuff is a per-display thing. I am not familiar with how color management occurs in Godot.

The common flow for a DP or a lighting artist nowadays is to use a Waveform (luma or RGB Parade) and a false color LUT to eyeball for any obvious issues.

This all probably all a diversion from physical lights, though, since it's tied to final pixel values rather than scene-referred (light) energy.

@TokisanGames
Copy link

TokisanGames commented Mar 25, 2022

Lighting

Examples of light presets:

  • northern sky (or night sky): 9000 kelvin directional light
  • 5600k LED: a medium-hard shadow, smooth diffusion, low throw, faster falloff area light
  • Standard incandescent: a hard shadow, long throw, 3200k spot light
  • Kino flo flourescent: a soft shadow, medium throw, slower falloff, diffuse, narrow rectangle area
  • Soft box: a soft shadow, smooth diffusion, medium throw, large square area

Discussion:

Lux and illuminance should not be used for non-directional lights as they measure light received on a surface, baking in distance and fall off. Only lumens and candelas should be used as they measure light emitted. 

There is a difference between the two, but can probably be ignored... (expand spoiler if you care) There is a difference between the two when it comes to spotlights and lasers, as lumens measures the omnidirectional output of the bulb, while candelas measures the density of light output, which may be higher when focused in a single direction like with a spotlight or laser. In such cases the ratio of candelas to lumens will be higher than in an open bulb, omni directional configuration. This difference can probably be ignored for our purposes. 

Filmmakers are concerned with these (excluding things we can ignore like CRI and wattage):

  • color temperature
  • lumens
  • hardness (of shadows and the edge of the spot light beam)
  • throw (attenuation distance, but there is also the fall off from the center of a spotlight beam to the edge)
  • diffusion (an open face spot light has greater intensity variation center to edge, whereas a fresnel lens gives a flat intensity center to edge)
  • light control (light blocking: barn doors attached to the light and flags on stands in front of the light. This can be done by the user in a scene, as long as light leaking is fixed and shadows are correct)

Here is a typical filmmaking light bulb for a 1k Arri lite or mole richardson. It lists 28,000 lumens, and 3200k color temp. Virtually every bulb you can buy at the store, even for home use, lists lumens and most provide color temp. These should be the standard setting on physical lights, depending on factors.


@Polyrhythm 

Third, it doesn't make sense to arbitrarily pick lumens over, say, candelas

Both should be provided. I would default to lumens, with a one click button to change units, or provide a project setting where units can be defined for lights and other measurements like other systems do.

Physical lights will be used by people trained in filmmaking and photography, DPs, and cinematographers who buy light bulbs or rent light kits based in part on the lumen output printed on the package. Candelas aren't given. Some light meters can measure lumen and candela output, however most DPs and photographers use them only for camera settings.

Here's how I would refactor the whole thing:

I don't disagree with the rest, and have provided some points of clarification.

Directional lights
Illuminance, lux

When we measure the light on the ground, the distance and fall off have already been baked in. In the spoiler below I consider not using lux, but as Polyrhythm stated, it makes sense here. The unit of illuminance is lux, so both aren't necessary. Lux is lumen/sq meter of the surface. The brightest sunlight might be 120,000 lux, an overcast at mid day 15,000 lux, sunrise 40 lux.

The physically correct alternative... (spoiler) The more physically correct alternative is to render DLs as the area lights (sky) or omni lights (stars) that they are, and provide their values in lumens and candela per sq meter that are emitted from them. The surface of the sun is 2 billion nits (units discussed below in area), a partly sunny sky 30,000 nits, and a clear sky 3,000 nits. However then we'd have to calculate fall off from 93M miles away, and it's probably not worth the extra math in a real time rendering environment.

ref p26, 37
https://cs184.eecs.berkeley.edu/cs184_sp17_content/lectures/14_radiometry/14_radiometry_slides.pdf

Spot or omni lights
Luminous flux, lumen or candela

Clarification: the unit of luminous flux is lumens. So only lumens and candelas are needed.

Area light
Luminance lm or cd/m2 (if you don't normalize this, the intensity will change based on the size of the light)
Emissives

Clarification:
1 lm/m2 sr (lumen/square meter steradian) == 1 cd/m2 (candelas/square meter) == 1 nit

So perhaps you could provide a unit toggle, but the numbers won't change. That might be confusing for users. On the other hand if you don't provide lumens and candela derived units, it might be confusing why I can set omnis in lumens, DLs in lux, and areas only in candelas or vica versa.

Light meter
For measuring all this crap. Spot meter mode that measures luminance (cd/m2 or nit) and EV of any surface.

Luminance measures the emitting surface. A spot meter is used when you point the camera at a light source or someone's face and read out the amount of light hitting the sensor (which bakes in attentuation).

Illuminance meter mode that reads lux off of any surface (valid only if materials are turned pure white).

Illuminance is the receiving surface, measured when you put a light meter on someone's face and read how much light is hitting the surface, so you could use a meter that follows the mouse where you measure light based on the surface your cursor is pointing at.

Or we could use zebras as I mentioned or false color as @Polyrhythm mentioned, as these could serve as a full-screen meters and provide the visual debugging that every major DCC renderer and video editing suite has. It is a bit of scope creep, but some of these could be considered or pushed to a post processing proposal:

  • histogram for luminance, r/g/b - brightness of values in verticle columns
  • waveform luminance/RGB parade
  • vector scope - for hue and saturation
  • zebras - highlights (Poly - tonemappers are not an issue, we use them to avoid blowouts, so they will still only flag over exposure)
  • False color meters
    • spot meter false color showing light hitting the camera
    • illuminance meter false color showing light hitting the surfaces

@HaSa1002
Copy link

Physical lights will be used by people trained in filmmaking and photography, DPs, and cinematographers who buy light bulbs or rent light kits based in part on the lumen output printed on the package.

I just wanna throw in that this feature should not only directed at those people. As Godot is heavily used by indies which either do game dev as hobby or have some education in one of the game dev fields (if any [see hobby]), this statement simply holds not for all the people that are going to use this feature. I get that you want to have great control but Godot is not an entreprise product that requires years of training and experience to get going. I don't have that education, yet I regularly use light units in other game engines to create working lighting situations. Please keep this in mind while proposing improvements. I feel like this either needs custom controls or some sort of simplified setting mode.

@TokisanGames
Copy link

TokisanGames commented Mar 25, 2022

@HaSa1002 Don't misunderstand. The tools should be designed to professional standards, and be physically correct. No one has suggested you should not be able to use them without training or that they should be difficult to use.

When the average person buys a light bulb they likely never look at the lumens rating, nor even the color temperature, but they are there for those who know what they mean. And nearly every camera, even on our phones, have not only auto mode but also manual settings for ISO, white balance and shutter speed. Yet no one suggests indie filmmakers can't use basic light bulbs and phones for filmmaking or require special training.

Yet if we were making a camera, it would be a disservice to both amateurs and professionals to make one without manual settings, or worse, with wrong or made up, non industry standard units because we didn't know what we were doing.

Edit: @HaSa1002 The point you quoted out of context was specifically about using lumens vs candelas as primary units. That wasn't expected to be relevant to those without experience using them.

@MattHorn25
Copy link

@HaSa1002 Don't misunderstand. The tools should be designed to professional standards, and be physically correct. No one has suggested you should not be able to use them without training or that they should be difficult to use.

I feel I should mention: for the first iteration of physically based light it doesn't really matter how much is exposed to users or in what way, easy or difficult. The key is to first make it as physically correct as possible under the hood, because that's what will be most difficult to change later on once users become accustomed to the implementation and certain behaviors become expected. If that's done first, then future Godot releases can concentrate on improving how that functionality is exposed to users in a comprehensive and friendly way, but the functionality itself could remain consistent across versions.

@me2beats
Copy link

Can it be named PhysicalCameraLens, PracticalCameraLens?

@TokisanGames
Copy link

Why is lens in the name at all? Many of the attributes specified such as ISO, exposure mode, shutter speed, and sensor dimensions I suggested have nothing to do with lenses. They are part of the sensor, shutter, physical attributes, or electronics. Camera attributes, settings or properties maybe.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: Done
Development

Successfully merging a pull request may close this issue.

10 participants