Gamma Space

Introduction to Gamma Space

SmallLines.png
(Don’t resize your browser)

The left and right are alternating horizontal lines of white and black. If you have a well-calibrated monitor, the middle-upper square should be much darker, and the middle-bottom square should be about the same intensity as the alternating lines. On the left and right side, you get essentially half-way between 0 and 255. So the color that is half-way between should be 127/128, right?

SmallLines-WithNumbers.png

Nope. Actually, 187 is about half-way between 0 and 255. 128 is about much, much darker than half.

ColorRamp.jpg

Here is a gradient from 0 to 255. Said another way, 128 is not actually half-way between 0 and 255. Rather, 187 is. Why? Well, there is this thing called gamma space.

GammaLines.jpg

When an output value is send to a monitor, everyone always naturally assumed that the number of photons would increase linearly with the number sent. In other words, the value 50 should send twice as many photons as the value 25. But in reality most monitors follow a gamma curve of about 2.2. If we think of our output value as being in-between 0 and 1.0 instead of 0 and 255, the number of photons will be about proportional to pow(x,2.2).

Why not just store these values as linear? In a typical 8 bits per channel representation we can only represent 256 levels of bright per channel and our eyes perceive more contrast in the blacks. A Gamma space allows to represent dark colors with more precision. Suppose we look at the 0-63 range. What happen if the displays were linear?

GammaVSLinear.png
Gamma (top) VS. Linear (bottom)

The Importance of Calculate the Illumination in Linear Space


GammaVSLinearIllumination.jpg
Left: Gamma illumination. Right: Linear illumination.

Gamma seems correct, but actually is not. Take a look at this capture from Avatar®

LinearIsBetter.png

The image on the left is wrong for several reasons. First, it has a really, really soft falloff, which does not look correctly. Also, you can see a lot of hue-shifting, especially in the specular highlight. It seems to shift from white to yellow to green and back to yellow. When you look at the image on the right, it looks like a diffuse surface with a white specular highlight. It looks like what the lighting model says it should look like. Finally, I’m not talking about what looks “good”, I’m talking about what is “correct”. That’s an entirely different discussion.

Gamma Space in XNA FINAL Engine


XBOX 360 and PC use two different gamma conversion hardware functions. Moreover, XNA does not support dedicated hardware gamma correction (because that is not supported consistently across Xbox and different Windows GPUs). For that reason the engine has a couple of conversion functions in the shader code.
Ideally they look like this:

// Converts from linear RGB space to gamma.
float3 LinearToGamma(in float3 color)
{
    // pow(x, y) is traduced as exp(log(x) * y). If x is 0 then log(x) will be –inf. 
    // So I have to avoid the pow(o, y) situation somehow.
    color = max(color, 0.0001f);
    return pow(color, 1/2.2f);
}
// Converts from gamma space to linear RGB.
float3 GammaToLinear(in float3 color)
{
    // pow(x, y) is traduced as exp(log(x) * y). If x is 0 then log(x) will be –inf. 
    // So I have to avoid the pow(o, y) situation somehow.
    color = max(color, 0.0001f);
    return pow(color, 2.2f);
}

Unfortunately with these you also have the problem that filtering and blending will be performed in gamma space, and there’s not much you can do about that (aside from doing the filtering and blending yourself, but that would be way too expensive).
Furthermore, these functions are too costly to do, instead the engine allows you to round down the 2.2 to 2.0. This gives you a simple square operation for conversion to linear (you can just multiply the value with itself), and a sqrt operation for conversion to gamma.

// Converts from linear RGB space to gamma.
float3 LinearToGamma(in float3 color)
{
    return sqrt(color);
}
// Converts from gamma space to linear RGB.
float3 GammaToLinear(in float3 color)
{
    return color * color;
}

The colors (which include textures) have to be converted from gamma to linear, and the final result (a linear color) has to be converted from linear to gamma.

References


http://www.valvesoftware.com/publications/2008/GDC2008_PostProcessingInTheOrangeBox.pdf
http://mynameismjp.wordpress.com/2009/12/31/correcting-xnas-gamma-correction/
http://darkchris.ath.cx/papers/Uncharted%202%20-%20HDR%20Lighting.pdf
http://http.developer.nvidia.com/GPUGems3/gpugems3_ch24.html


Last edited Jan 22, 2013 at 3:02 PM by jischneider, version 27

Comments

No comments yet.