Day 16 - ⚡️ Quick - Gamma Correction

Article / 21 May 2026

Every image you've ever created on a computer was made in the wrong color space. Not wrong enough to look broken - but wrong enough that the physics underneath your lighting or shading math doesn't add up. That's gamma. This is a somewhat high-level overview of why it exists and where it tends to break.

Why Monitors Lie About Brightness

Monitors don't display light linearly - they apply a power curve before output (~2.2 for monitors, ~2.4 for TVs). This started as a physical property of CRT displays: doubling the input voltage didn't double the brightness. That non-linearity happened to closely match how human eyes perceive brightness, so the convention stuck long after CRTs disappeared. In 1996, HP and Microsoft formalized it into the sRGB standard - which is why almost every image file and display today still operates in this space.

John Hable's Linear-Space Lighting post on Filmic Worlds (originally a GDC talk) illustrates this with a simple test: the perceptual midpoint between 0 and 255 is not 128 - it's 187. A value of 128 looks much darker than halfway. That's the gamma curve in action. It also explains why every photo on your hard drive is already gamma-encoded: cameras apply the inverse curve at capture (pow(x, 1/2.2)) so the stored image is bright and pastel-ish, and the monitor's own gamma curve brings it back to correct at display time.

The result for rendering: if your renderer works in linear light and outputs without correction, the image looks washed out and overexposed. Gamma correction applies the inverse curve before the signal hits the display, so what you see matches what the renderer calculated.


187 is the perceptual midpoint between 0 and 255 - not 128. Via John Hable / Filmic Worlds.

Left: gamma-space lighting - soft, incorrect falloff, visible hue shifting in specular. Right: linear-space lighting - harsh falloff that matches physical reality. Via John Hable / Filmic Worlds.

Why Linear Space Matters for PBR

PBR math assumes linear light values - doubling the intensity should double the result. If your textures are in sRGB (gamma-encoded) and you feed them into the shader without converting, the math breaks. The albedo looks too bright, lighting doesn't accumulate correctly, and specular highlights shift hue. Hable specifically notes the white specular highlight drifting from white to yellow to green and back in gamma space - a diagnostic signal that the pipeline is operating in the wrong space. Everything needs to be calculated in linear; the final gamma conversion happens at the very end, based on the target display.

Without a proper linear pipeline, the gamma errors in input and output partially cancel each other out - which is why gamma-incorrect projects can still look acceptable. It's two wrongs making a right, and it holds up until the lighting gets complex enough to expose the cracks.

The volleyball comparison in Hable's post makes this concrete: linear-space lighting produces a harsh falloff that matches the reference photo; gamma-space produces a soft, incorrect falloff that looks plausible but doesn't match reality.


Top: linear-space lighting. Bottom: gamma-space. The harsh falloff of the linear version matches the real photo. Via John Hable / Filmic Worlds.

The same logic applies to exposure and tonemapping - both depend on linear values being correct before the final encode.

High Dynamic Range

It gets more complex with HDR TVs and monitors. They don't follow a simple gamma curve - HDR uses standardized transfer functions instead: PQ (Perceptual Quantizer) for most HDR10 content, and HLG (Hybrid Log-Gamma) for broadcast. These are designed to map a much wider luminance range to the display, not just correct for CRT legacy. A renderer targeting HDR output needs to account for this at the end of the pipeline rather than applying a standard gamma 2.2 encode. In practice this means checking your engine's HDR output settings and testing on both SDR and HDR displays - the same content can look noticeably different between the two.

Where It Breaks

Most gamma issues don't announce themselves - they show up as subtle wrongness that's hard to trace back to a source. These are the most common places the chain breaks.

  • Textures flagged incorrectly - albedo should be sRGB, roughness/metalness/normals should be linear. Marking a normal map as sRGB is a fast way to get subtly wrong shading that's hard to diagnose. Most engines handle the conversion automatically if the texture is flagged correctly, but make sure your shader system is actually converting on sample - otherwise the flag is meaningless.
  • Texture compression - enabling gamma space on albedo compression is correct, since BC compression algorithms prioritize blocks based on perceptual importance. The same approach doesn't apply to normal maps, which need to be treated as linear data.
  • Compositing outside the renderer - taking a linear render into Photoshop without converting first means every layer blend operates in the wrong space.
  • Manual color values in shaders - typing 0.5 into a linear input is not the same as 128 sRGB. A common source of "why does this look different in-engine?" confusion.
  • Light attenuation - before linear workflows were standard, artists used linear falloff (1/distance) instead of the physically correct quadratic (1/distance²) because quadratic looked too harsh on screen. With proper gamma correction in place, quadratic suddenly gives correct results.
  • Exposure and post-processing - exposure, bloom, color grading, and tonemapping all need to operate on linear values to produce correct results. Exposure applied in gamma space lifts and clips differently than it should - highlights compress too early and shadows respond unevenly. Bloom is a clear example: in gamma space, bright areas are already compressed, so the bloom spreads less than it physically should. In linear space, bright pixels carry their actual intensity, and the effect behaves correctly. The right order is always: render linear → post-process linear → tonemap → gamma encode → output.

Get these right and gamma stops being a source of mystery bugs.

© 2026 Stefan Groenewoud - All views are my own, not those of my employer.