I’m working on adding HDR output support to the Godot game engine and want to ensure that everything is setup up to correctly implement ACES 2 in the future, either as an add-on or integrated into the core of the engine.
I am relatively new to the world of HDR output, so please point out if I may have any incorrect assumptions or understandings! Also, I happen to be at GDC next week and would love to chat in person if that might be easier
Typically a game engine will obtain reference and maximum luminance of the display from the OS.
The reference luminance is used as the “white” (1.0) value for mapping SDR content to the dynamic range of the display. This means that OS windows will render within the reference luminance range and a game’s GUI is expected to be within this range as well. The reference luminance is often exposed to the player, which allows the player to adjust the brightness of the image if they find that the default OS reference luminance is not suitable for their game use. BT.2408 recommends this reference luminance to be about 203 nits, so games may default to 200 nits as a player-facing value. I noticed that my Windows computer with a Sony TV reports 240 nits as its reference luminance.
The maximum luminance is ignored in a scenario where there is no tonemapping and values are clipped or tonemapped by the display (the scene referred values are passed directly onto the display). When tonemapping, the maximum luminance is used to determine how the “shoulder” of the tone curve should behave. This is primarily relevant to input values above middle grey 0.18, and most relevant to values above 1.0 (reference luminance). Again, this luminance value can be configured by the user in most games because how this value is best configured may be determined by how their display is tonemapping its input values.
With these two values, the range of output values can easily be determined as [0, (max_luminance / ref_luminance)]
, which would give a range up to 5.0 in the reasonable scenario of ref_luminance = 200
and max_luminance = 1000
. This also means that SDR content is simply [0, 1]
because ref_luminance
and max_luminance
are both 200 nits.
So this sort of variable dynamic range determined by reference and maximum luminance gives the player reasonable control over the brightness of their game and also allows them to adjust the HDR behaviour of their specific display.
Onto ACES 2: I see that the source is provided as CTL files. Thanks! I’ve been able to run the tool and reproduce the test images exactly, which gives me a great starting point for making a simplified approximate of ACES 2 that is suitable for running in a game engine that must run on web and mobile platforms with very little processing resources leftover for tonemapping.
–
EDIT:
After reviewing some of yesterday’s meeting, I understand the following approach is incorrect, as these constants should not be modified directly and new optimizations are yet to make their way to the CTL reference implementation. My mistake! Please feel free to disregard the following original text:
Regarding variable reference/max luminance, I expect I can simply modify the following constants to see what the behaviour should be with different reference and max luminance values:
TSParams:
const float n_r // normalized white in nits (what 1.0 should be)
const float c_d // output luminance of 18% grey (in nits)
General constants:
const float peakLuminance // luminance the tone scale highlight rolloff will target in cd/m^2 (nits)
Is this correct?
To test my understanding, I tried the following: Change the reference and maximum luminance values for Output.Academy.Rec709-D65_100nit_in_Rec709-D65_sRGB-Piecewise.ctl from 100/100 to 200/200:
const float n_r = 200.0;
const float c_d = 10.013 * (n_r / 100.);
const float peakLuminance = 200.;
This should result in the same image data in linear [0,1]
range. When mapping to the screen a 200 nits reference luminance will be used to multiply the values into nits units. But when I tried this, I noticed the resulting TIFF file appears brighter. Maybe I’m just misinterpreting the TIFF data? Or is there something else about this that I’m not understanding?
Thanks!