Using gamma correction to improve image quality

It is common for many books about OpenGL and 3D graphics to somewhat neglect the subject of gamma correction. Lighting and shading calculations are performed, and the results are sent directly to the output buffer without modification. However, when we do this, we may produce results that don't quite end up looking the way we might expect they should. This may be due to the fact that computer monitors (both the old CRT and the newer LCD) have a non-linear response to pixel intensity. For example, without gamma correction, a grayscale value of 0.5 will not appear half as bright as a value of 1.0. Instead, it will appear to be darker than it should.

The lower curve in the following graph shows the response curves of a typical monitor (gamma of 2.2). The x axis is the intensity, and the y axis is the perceived intensity. The dashed line represents a linear set of intensities. The upper curve represents gamma correction applied to linear values. The lower curve represents the response of a typical monitor. A grayscale value of 0.5 would appear to have a value of 0.218 on a screen that had a similar response curve.

Using gamma correction to improve image quality

The non-linear response of a typical monitor can usually be modeled using a simple power function. The perceived intensity (P) is proportional to the pixel intensity (I) raised to a power that is usually called "gamma".

Using gamma correction to improve image quality

Depending on the display device, the value of gamma is usually somewhere between 2.0 and 2.4. Some kind of monitor calibration is often needed to determine a precise value.

In order to compensate for this non-linear response, we can apply gamma correction before sending our results to the output framebuffer. Gamma correction involves raising the pixel intensities to a power that will compensate for the monitor's non-linear response to achieve a perceived result that appears linear. Raising the linear-space values to the power of 1/gamma will do the trick.

Using gamma correction to improve image quality

When rendering, we can do all of our lighting and shading computations ignoring the fact that the monitor's response curve is non-linear. This is sometimes referred to as "working in linear space". When the final result is to be written to the output framebuffer, we can apply the gamma correction by raising the pixel to the power of 1/gamma just before writing. This is an important step that will help to improve the look of the rendered result.

As an example, consider the following images. The image on the left is the mesh rendered without any consideration of gamma at all. The reflection model is computed and the results are directly sent to the framebuffer. On the right is the same mesh with gamma correction applied to the color just prior to output.

Using gamma correction to improve image quality

The obvious difference is that the left image appears much darker than the image on the right. However, the more important distinction is the variations from light to dark across the face. While the transition at the shadow terminator seems stronger than before, the variations within the lighted areas are less extreme.

Applying gamma correction is an important technique, and can be effective in improving the results of a lighting model.

How to do it...

Adding gamma correction to an OpenGL program can be as simple as carrying out the following steps:

  1. Set up a uniform variable named Gamma and set it to an appropriate value for your system.
  2. Use the following code or something similar in a fragment shader:
    vec3 color = lightingModel( … );
    FragColor = vec4( pow( color, vec3(1.0/Gamma) ), 1.0 );

If your shader involves texture data, care must be taken to make sure that the texture data is not already gamma-corrected so that you don't apply gamma correction twice (refer to the There's more… section of this recipe).

How it works...

The color determined by the lighting/shading model is computed and stored in the variable color. We think of this as computing the color in "linear space". There is no consideration of the monitor's response during the calculation of the shading model (assuming that we don't access any texture data that might already be gamma-corrected).

To apply the correction, in the fragment shader, we raise the color of the pixel to the power of 1.0 / Gamma, and apply the result to the output variable FragColor. Of course, the inverse of Gamma could be computed outside the fragment shader to avoid the division operation.

We do not apply the gamma correction to the alpha component because it is typically not desired.

There's more...

The application of gamma correction is a good idea in general; however, some care must be taken to make sure that computations are done within the correct "space". For example, textures could be photographs or images produced by other imaging applications that apply gamma correction before storing the data within the image file. Therefore, if we use a texture in our application as a part of the lighting model and then apply gamma correction, we will be effectively applying gamma correction twice to the data from the texture. Instead, we need to be careful to "decode" the texture data, by raising to the power of gamma prior to using the texture data in our lighting model.

There is a very detailed discussion about these and other issues surrounding gamma correction in Chapter 24, The Importance of Being Linear in the book GPU Gems 3, edited by Hubert Nguyen (Addison-Wesley Professional 2007), and this is highly recommended supplemental reading.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.129.19.251