Tuesday, October 22, 2013

Tech Feature: HDR Lighting


Hello my name is Peter and I’m the new graphics and engine programmer. New is not truly the right word because I have been operating at Frictional for a year now. Throughout this time I have updated the engine and added a lot of new graphic characteristics. This will be the first of my blog posts descripting the modifications that have been made.


One of the largest changes to the the new engine is the introduction of HDR (High Dynamic Variety) Lighting. This is a technique to improve the detail of the lighting technique. The benefit of making use of HDR is that vibrant items can be truly vibrant, dark factors can be actually dark, and particulars can be observed in both.

In nature there is no limit to how vibrant one thing can be. The difference among a 60 W light bulb and sunshine hitting the earth is about ten 000 luminance (cd/m^two). This means that we need a way to shop higher intensity values while keeping the top quality and precision of the dark regions. Thankfully there already exists a approach for storing such values - by using floating point numbers.

We use a 16-bit fp RGBA buffer to retailer our lighting. This gives us adequate of a dynamic variety with out taking up as well a lot memory.

Tone mapping

A normal computer monitor can display 8-bit colors in between the value of [..1]. Because of this the monitor can not display a 16-bit HDR image directly. To be displayed the image will have to be converted to 8-bit whilst maintaining as a lot of the details as feasible.

Tone mapping is the approach of converting an image of dynamic range to a single with a clamped range among [..1].

The simplest approach for carrying out this is to use the Reinhard tone mapping algorithm.

vec3 colour = x / (x + 1.)

No matter how higher x gets the final value will usually keep in between [..1]. The dilemma with Reinhard is that it desaturates your dark colors and removes contrast. In the brighter parts of the image Reinhard produces wonderful result with its soft highlights.

What you want to have is an algorithm that preserves the saturation of the color in the dark areas and that keeps as significantly of the contrast as possible.

I ended up picking an algorithm produced by John Hable for Uncharted 2.

vec3 Uncharted2Tonemap(vec3 x)

float A = .15
float B = .50
float C = .10
float D = .20
float E = .02
float F = .30

return ((x*(A*x+C*B)+D*E)/(x*(A*x+B)+D*F))-E/F
Left: Lengthy curve, Appropriate: S-curve close to dark values

This algorithm is based on a filmic tone mapping algorithm designed by Kodak. It keeps the good highlights from Reinhard although using a slightly S-shaped curve also maintain the dark colors saturated.


If you have been sitting in a dark area for some time and stroll out into the sun, your eyes will not be prepared for the bright light and you will have to squint. After a although your eyes will have adjusted to the light and it will not bother you anymore.

Exposure is a way to handle the intensity of light that gets passed through the lens. In the eye this is controlled by the size of the pupil and in a camera it is done by deciding on for how long the sensor must be active.

There are a few distinct approaches to control exposure in a game. It can be controlled automatically by storing the average luminance more than a handful of frames and calculating a exposure from that worth.
We pick to go with a significantly easier method that lets the artists handle the exposure by dividing the level into regions that have various exposures. So in a dark area the exposure can be increased and in an outside region the exposure can be decreased.

vec3 colour = Uncharted2Tonemap(scene_color * exposure)

White Point

A white point is used to increase the contrast of the image. This is the value that is selected to be the brightest any pixel can be. Pixels brighter than the white point will be clamped to 1..

vec3 color = Uncharted2Tonemap(scene_colour * exposure) / Uncharted2Tonemap(white_point)

HDR Bloom

When a pixel is brighter than the white point it can be utilized to produce an additional post effect named HDR Bloom. Pixels that get too vibrant need to start bleeding more than to other nearby pixels, generating a halo about them. It is a subtle impact that adds realism to the image.

To resolve this I use a couple of render passes. Very first a vibrant pass is applied to the image which removes all pixels that are under the white point and scales down all the pixels that are above it. The result is then blurred to generate the halos. The blurred image is then added to the original image. The HDR Bloom effect must be performed ahead of the tone mapping.

Final Thoughts

I would say that HDRL and filmic tone mapping is the most important portion of any rendering pipeline. It greatly increases the quality of lighting and will make your game appear a lot far more realistic.

But HDR and tone mapping is all for nothing at all if your calculations are not carried out in linear colour space. My subsequent tech function will concentrate on gamma correction and the linear colour space.


1 comment: