WebGL Color Management

Learning how to input and output color correctly

Unbeknown to you, 99% of the images on your computer are in sRGB color space. You name it, jpg, png, etc. All of them are most likely in sRGB color space.

However, when you render something in WebGL, light calculations, for example, you need to work in linear-sRGB color space and output in sRGB color space.

The difference between linear-sRGB and sRGB is that the lather has gamma correction applied to it. Gamma correction makes it non-linear.

To use these sRGB images in our linear workflow, we need to remove the gamma correction when sampling the image in the shader. Then, once everything is said and done, re-add the gamma correction back for the final output of the shader.

Some textures are already in Linear-SRGB while some others are in sRGB. Most of the time, ThreeJS makes this a non-issue.

However, sometimes models don't have the correct color space. Either because the 3D software sets the incorrect color space, or because you had to load images manually. This is when understanding color management is important.

Getting started

sponsor

Data Textures in Linear-sRGB

If you come from the far reaches of userland like me, you'll be surprised to hear that not all images have color. There are non-color images like Metalness maps.

This means that this texture isn't in any color space. While you can visualize the image in any image viewer, these are not actual colors and therefore don't require any color transformations:

  • Metalness Maps
  • Roughness Maps
  • Normal Maps
  • Ambient Occlusion
Jelly Lights by yozic

Color Textures in sRGB

Some images do represent colors. And more often than not, they are in sRGB color space.

  • Emissive maps
  • Albedo Color (color maps)
  • CSS colors
  • Most regular images you have in your computer.

These textures need to be correctly transformed into linear space before being used for light calculations or shading.

In those cases, identify the correct image format and set it on the texture

texture.colorEncoding = THREE.SRGBColorEncoding

CSS hex colors are also in sRGB. Three.Color is aware of this and transforms them into linear when they are detected.

Mind Flowers by mrange

Renderer output

Most of the time, when rendering color, you want the default encoding.

renderer.outputEncoding = THREE.SRGBColorSpace

In case you want to use three to render some data texture (maybe something you'll use for another render), set the output encoding to

renderer.outputEncoding = THREE.NoColorSpace

While renderer.outputEncoding is set on the renderer, this setting tells all the shaders to transform their final output from Linear to sRGB as a final step. The work is all done by the shaders.

Ripple color by masuwa

The Photoshop problem

Photoshop, and programs alike, are made to modify color images. They will often import and export your image in sRGB even though your metalness Map may be in Linear-SRGB.

A quick fix for these cases is to label your Metalness map as sRGB and let the renderer take care of the conversion.

However, the most appropriate fix is to indicate to Photoshop that you don't want this image imported or exported as sRGB.

Evil Membrane by leon

Further reading / Inspiration