HDR rendering techniques in games have become very commonplace. To achieve realistic lighting, one sets the brightness of lights in-game to realistically proportioned values, and then uses a post-processing tone-mapping shader to map it back down to the standard 0-255 sRGB range of the monitor.
The only limitation of this technique is that the result is tonemapped down to that narrow 8-bit dynamic range before it reaches our monitor (and by extension, our eyes). The sRGB gamma curve helps a tiny bit to add a bit more range to dark tones, but not much. However, there are now some TVs on the market that are capable of much wider dynamic ranges, and can represent the brightness of specular highlights far more accurately and impressively. Though good HDR monitors capable of a truly interesting dynamic range are rare and expensive (upwards of $1000), their price has been decreasing and will likely to decrease even more in the future.
I’ve had some luck getting Panda to work with this, so I just wanted to share my findings in case others wanted to experiment with it as well.
What do you need:
- Windows 10, with HDR mode switched on. Sadly, other operating systems just aren’t ready yet.
- A TV or monitor supporting SMPTE 2084, often labelled HDR10. Now, there are many monitors that claim to be HDR but don’t go above 400 nits of brightness, and I don’t really think this is worth it; you really need a DisplayHDR1000 TV or monitor to be able to see a significant effect.
- A game that already uses HDR rendering (panda3d-simplepbr will do), with bright lights.
- NVIDIA Pascal GPU (Maxwell may also work). I have not tested AMD.
- Latest Panda master.
Now, to enable HDR output in Panda, you need to do this: (requires latest Panda master)
When you do this, you will immediately notice two things:
- The scene becomes dark.
- The colours will look washed out.
The reason for (1) is that the colours are now using absolute brightness, instead of relative brightness. Instead of the value 1.0 meaning “the brightest that the monitor can go”, it means “80 nits” (1 nit = 1 cd/m2). (Some monitors will even disable brightness settings in HDR mode.) Fortunately, the framebuffer values are no longer capped to 1.0. If your monitor goes up to 1000 nits, as a decent HDR monitor will, then you can write a value of 1000 / 80 = 12.5 to the framebuffer to get the full brightness. You will need to adjust your tonemapper to take this into account.
The reason for (2) is that the framebuffer output is no longer expected to be using the sRGB gamma curve, but is expected to be linear (aka scRGB). If you were using
framebuffer-srgb true, simply remove it. If you had a custom gamma correction in your shader, simply remove it. If you were not using a gamma-correct pipeline to begin with, well, it’s no longer optional: set your input color textures to the sRGB format.
Here are some resources to get started with this subject:
It would be good to think about ways we can build a good Panda API around this. We probably will need an interface to communicate about the color space of the framebuffer (sRGB vs scRGB vs BT.2020 etc.) We will also want to create an interface to send HDR metadata, such as reference brightness and primaries, to the monitor (right now this is only possible through proprietary AMD/NVIDIA APIs). Food for thought!