Three.js Color Space and Gamma Correction Guide

This article provides a clear overview of gamma correction and explains how modern Three.js handles output color spaces. You will learn the fundamental physics behind linear and non-linear color perception, why gamma correction is necessary for realistic 3D rendering, and how to correctly configure color spaces for renderers and textures in recent versions of Three.js.

Understanding Gamma Correction

Computer monitors display colors non-linearly, meaning doubling the input voltage does not double the physical brightness of the screen. At the same time, human vision is highly sensitive to changes in dark tones but less sensitive to changes in bright tones. To account for this, digital images are encoded in a non-linear format (usually sRGB) to allocate more data to the dark tones humans can actually perceive.

However, 3D engines must perform lighting calculations (shading, reflections, and blending) in a mathematically accurate “linear” color space where light behaves naturally (double the light energy equals double the physical brightness).

Gamma correction is the process of translating between these spaces: 1. Decoding: Non-linear input textures (like sRGB color maps) are converted to linear space so the renderer can perform accurate lighting calculations. 2. Encoding (Gamma Correction): The final linear render is converted back to sRGB space before being sent to the monitor, ensuring the output looks correct to the viewer. Without this final correction, renders often appear washed out, overly dark, or incorrectly saturated.

Color Space Management in Modern Three.js

In older versions of Three.js, developers managed encoding using renderer.outputEncoding. In modern Three.js (version r152 and later), the API has been streamlined to use standardized color space terminology.

1. The Renderer’s Output Color Space

By default, modern Three.js configures the WebGL renderer to output to the sRGB color space. This means gamma correction is automatically applied to the final image. The renderer property used to control this is outputColorSpace:

const renderer = new THREE.WebGLRenderer();
// This is set to THREE.SRGBColorSpace by default in modern versions
renderer.outputColorSpace = THREE.SRGBColorSpace; 

If you are rendering to a post-processing pipeline that expects linear input, you may temporarily set this to THREE.LinearSRGBColorSpace, but the final output shown on screen should almost always be THREE.SRGBColorSpace.

2. Texture Color Spaces

To ensure the renderer does calculations in linear space, you must tell Three.js how to interpret incoming textures.

By properly defining the input color spaces of your textures and letting the renderer handle the output color space, modern Three.js guarantees physically accurate lighting, consistent colors, and visually correct 3D scenes.