Gamma Correction

From Horde3D Wiki
Revision as of 18:42, 2 January 2010 by Marciano (talk | contribs)
Jump to: navigation, search


Gamma Correction

Being aware of and respecting gamma is essential for achieving superior rendering quality. Proper gamma correction and color management is an essential part of movie production and has become common in high profile games as well. Although basic gamma correction is easy to do, it can be pretty confusing at first for artists and even graphics programmers that have not dealt with it before.

Gamma Facts

  • CRT monitors have a non-linear response curve (rather exponential), hence an RGB value of 0.5/0.5/0.5 will not produce the expected gray of 50% brightness but rather about 25%
  • The eye is more sensitive to dark intensities and will see banding in darker areas more easily
  • The non-linearity of CRTs matches very well the visual perception since it gives more precision for darker shades (so gamma is a feature, not a bug)
  • Gamma was standardized as sRGB with an overall exponent of approximately 2.2 and is used for most output devices now, including TFTs
  • sRGB is not a different image format, it only defines how the color intensities stored in an image should be interpreted

Correcting Input Gamma

Most standard color textures (albedo/diffuse maps) are in gamma space (usually sRGB space) to make best use of the available 8 bits of precision. Photos are almost always in sRGB space (jpegs are corrected for a gamma of 2.2 by convention) and if you paint a texture using a standard graphics editor, it will inherit the gamma of your monitor. This is good to reduce banding artefacts but the problem is that most lighting computations done in the shaders assume linear values. For that reason, textures in sRGB space need to be linearized before being used in shaders.

It is easy to have Horde3D do that linearization automatically. All you need to do is indicating which textures are in sRGB space and enabling the engine option SRGBLinearization. To tag a texture as sRGB, you can add sRGB="true" to the corresponding sampler definition of a material, as shown below:


Material with sRGB albedo map
<Material>
    <Shader source="shaders/model.shader" />
    <Sampler name="albedoMap" map="myTex.dds" sRGB="true" />
</Material>


Note that usually only color textures (photos or hand-painted images) are stored in sRGB space. Textures that contain non-color data (like normal maps) should be in linear space and no conversion is required when they are sampled in the shader. Hence these textures should not be tagged as sRGB. It is also important to know that the alpha channel of an sRGB texture is always considered to be linear and is not transformed.

Correcting Output Gamma

Before the final rendered image gets displayed on the monitor, it has to be converted from linear space to sRGB. This conversion can easily be done in a post-processing shader by raising the linear color to the inverse gamma exponent:


Conversion of linear color to sRGB
finalColor = pow( color, 1.0 / 2.2 );


Linear color values should not be stored in 8 bit render targets since otherwise precision for the darker colors will get lost, resulting in noticeable banding. A 16 bit floating point render target, which is useful for HDR as well, should be used instead.

The following sample shows a basic HDR pipeline, where the gamma correction is applied after the tone mapping:


Sample HDR post-processing fragment shader
uniform sampler2D buf0, buf1;
uniform vec4 hdrParams;
varying vec2 texCoords;

void main( void )
{
	vec4 col0 = texture2D( buf0, texCoords );	// HDR color
	vec4 col1 = texture2D( buf1, texCoords );	// Bloom
	
	// Tone mapping (simple photographic exposure)
	vec4 finalCol = 1.0 - exp2( -hdrParams.x * col0 );
	
	// Apply bloom
	finalCol += col1;
	
#ifdef GAMMA_CORRECTION
	finalCol = pow( finalCol, 1.0 / 2.2 );
#endif
	
	// Output gamma-corrected result
	gl_FragColor = finalCol;
}


The #ifdef makes it possible to control the gamma correction from the application by configuring the shader preambles:


Enabling the output gamma correction from the application
h3dSetShaderPreambles( "", "#define GAMMA_CORRECTION" );


Finally, please note that the overlays are often drawn after the HDR post processing and would hence not be gamma corrected. Since overlays are usually just displayed without any further shading computations, a simple trick is to avoid tagging them as sRGB. Although this is logically not quite correct (overlays are color textures), it will avoid the conversion to linear space and hence also the need to convert from linear back to sRGB.

Final Words

If you don't do any gamma correction, the effects of wrong input and output gamma will **almost** cancel out each other and the result will look acceptable. However, the emphasis is on "almost". There will definitely be some visual glitches which are unpleasing if you aim for a high visual fidelity.

If you have not considered gamma before and try to switch to a fully gamma aware (production) pipeline, chances are high that some brightness values like the intensity of light sources or particle colors need to be reduced. This is because without output gamma correction, everything looks darker than it actually is, and you had to adjust your light sources to compensate for that.

Further Reading