glTF Base Color Texture Color Space Conversion

When sampling base color textures from a glTF 2.0 asset, rendering engines must convert the color data from the non-linear sRGB color space to a linear color space prior to performing lighting calculations. This article explains the exact color space requirements defined by the glTF specification, distinguishes between how color and alpha channels are handled, and details the standard methods for executing this conversion in real-time graphics pipelines.

The glTF Color Space Specification

The glTF 2.0 specification strictly mandates that color data intended for display—such as the base color texture (baseColorTexture) and emissive texture (emissiveTexture)—must be encoded in the sRGB color space.

However, physically based rendering (PBR) equations and lighting calculations require input values to represent linear physical light intensity. Sampling an sRGB texture directly without transformation distorts the math, resulting in washed-out highlights, harsh shadow transitions, and incorrect color blending.

Channel-Specific Requirements

Base color textures are packed as four-channel RGBA images, but the color space conversion applies selectively:

  • RGB Channels (Color): Stored using the sRGB transfer function. These three channels must be converted from sRGB to Linear space upon sampling.
  • Alpha Channel (Coverage/Opacity): Stored linearly. The alpha channel must not undergo sRGB-to-linear conversion. It represents linear opacity factors directly.

Conversion Methods

There are two primary ways to handle the sRGB-to-linear conversion in a rendering pipeline:

Modern graphics APIs (Direct3D, OpenGL, Vulkan, Metal, WebGL 2) provide dedicated hardware support for sRGB textures. When creating the texture resource, specifying an sRGB format ensures that the GPU automatically decodes the RGB channels to linear space during the texture fetch, leaving the alpha channel linear, with zero runtime performance cost.

Common API formats include:

  • Vulkan: VK_FORMAT_R8G8B8A8_SRGB
  • DirectX: DXGI_FORMAT_R8G8B8A8_UNORM_SRGB
  • OpenGL / WebGL 2: GL_SRGB8_ALPHA8

2. Manual Shader Decoding

If an sRGB texture format cannot be utilized and the texture is instead uploaded as a standard linear unorm format, the conversion must be performed manually within the fragment shader after sampling.

The precise standard sRGB to linear conversion formula defined by the IEC 61966-2-1 standard is:

vec3 sRGBToLinear(vec3 srgb) {
    bvec3 cutoff = lessThanEqual(srgb, vec3(0.04045));
    vec3 lower = srgb / 12.92;
    vec3 higher = pow((srgb + 0.055) / 1.055, vec3(2.4));
    return mix(higher, lower, cutoff);
}

For performance-constrained mobile environments where exact compliance is secondary to cycle count, a gamma 2.2 power approximation is frequently used:

vec3 sRGBToLinearApprox(vec3 srgb) {
    return pow(srgb, vec3(2.2));
}

Summary of Execution

  1. Load the glTF baseColorTexture using an sRGB-aware format or mark it for manual decoding.
  2. Sample the texture inside the shader.
  3. Transform the rgb components to linear space (automatically via texture sampler or manually via math).
  4. Keep the a (alpha) component unchanged.
  5. Multiply the resulting linear RGBA value by the glTF material's baseColorFactor (which is already defined in linear space) before feeding the values into the PBR BRDF functions.