Please send suggestions or corrections for this page to the author at sbuss@ucsd.edu.

This page describes the features of the OpenGL program GammaCorrectTest program. This is part of series of programs illustrating the usage of Modern OpenGL C++ programs and GLSL shaders. For more on gamma-correction and sRGB, see the chapter on Color in the book 3D Computer Graphics, by S. Buss (second edition only!).

I. What the program does

The basic goal of the program is to show how gamma correction or sRGB is used to convert from non-linear RGB values to linear RGB values. The underlying issue is that the human eye responds sublinearly to light levels. For example, when light intensity is doubled, the brightness as perceived by a person is less than doubled.

When the program is run, there are five rectangles on the lefthand side, and five rectangles on the righthand side. The lefthand rectangles are dithered. Numbering from the rectangles from the bottom as 0,1,2,3,4; the i-th rectangle has i out of every four pixels set to full brightness, and the remaining set fully turned off (black). Thus, the i-th rectangle has light intensity (say as measured in light energy or photon flux) at a level of 0%, 25%, 50%, 75% or 100%.

The righthand side images are set a fixed brightness level, the same for each pixel. Initially, they are set with RGB values equal to 0, 0.25, 0.5, 0.75 and 1.0. However, keyboard controls let you change the values either by adjusting the exponent gamma (γ) or by using linear-to-sRGB conversion.

Here is (a fairly poor quality version of) the image, in sRGB mode:


II. Usage

See the main page for GammaCorrectTest for a brief explanation of how to use the program. It can toggle in and out of sRGB mode. When not in sRGB mode, the color values are computed using the gamma function xγ: the value of γ (gamma) can be changed dynamically by dragging the mouse left or right.

III. New feature: glFragCoord in the fragment shader

The image rendered as single rectangle that fills the entire graphics window. All colors inside the rectangle are set by fragment shader, named gammaTester_frag. This shader code determines which rectangle of the 5x2 array the current fragment (pixel) lies inside. Based on this, it sets the pixel color.

For the dithering on the lefthand side, the fragment sets each pixel to have color 0.0 (black) or color value 1.0. For this, it needs to known the index of the pixel on the screen. It is not enough to know the x-y coordinates because they just give a value in the rectangle [-1,1]x[-1,1]. Which pixel this corresponds to depends on the size of the graphics window.

Fortunately, the fragment shader has a special input calls gl_FragCoord that gives the coordinates of (the center of the fragment. gl_FragCoord is a vec2 type variable. The values gl_FragCoord.x and gl_FragCoord.y are floating point values for the center of the pixel. For the bottom left fragment (pixel) gl_FragCoord.x and gl_FragCoord.y will both equal 0.5. Moving one pixel rightward, gl_FragCoord.x and gl_FragCoord.y will equal 1.5 and 0.5. Moving one pixel up from the bottom left, gl_FragCoord.x and gl_FragCoord.y will equal 0.5 and 1.5.

Converting these to integer coordinates for the pixel is done by computing int(gl_FragCoord.x) and int(gl_FragCoord.x).

The rest of the code in the fragment shader uses GLSL code features that are very similar to C++. The trickiest part is the code that decides how to dither bits within a 4x2 pattern of pixels to avoid forming too-obvious patterns of lines of pixels. But, this uses no new language features, so we do not describe it here. You are encouraged to read the code to learn how it works.