GLSL samplerCube: Cubemap Environment Mapping Guide

In OpenGL Shading Language (GLSL), samplerCube is a specialized uniform data type designed to handle cubemap textures, which project six distinct images onto the internal faces of a cube. This article covers the primary purpose of samplerCube, its declaration syntax, and how it is sampled within fragment shaders to produce realistic environment mapping effects such as skyboxes, dynamic reflections, and refractions.

Purpose of samplerCube

A standard 2D texture uses a pair of floating-point coordinates (u, v) to sample color data from a flat surface. In contrast, a cubemap consists of six square 2D textures forming an axis-aligned cube (+X, -X, +Y, -Y, +Z, -Z).

The samplerCube type enables the GPU to sample this composite structure using a 3D direction vector (x, y, z) cast from the origin of the cube. The hardware automatically determines which of the six cube faces the vector intersects and converts the remaining coordinates into local texture coordinates for that face. This makes samplerCube the industry standard for omnidirectional calculations, including:

  • Skyboxes and Environment Maps: Rendering distant backgrounds surrounding a 3D scene.
  • Specular Reflections: Simulating glossy or mirror-like surfaces reflecting the surroundings.
  • Refractions: Simulating light passing through translucent materials like glass or water.
  • Omnidirectional Shadow Maps: Generating point light shadows cast in all directions.

Declaration Syntax

In GLSL, samplerCube is declared as a uniform variable, typically within the fragment shader:

uniform samplerCube u_EnvironmentMap;

Because it is an opaque type, it cannot be modified directly within the shader code, nor can it be instantiated as a standard local variable. Its value represents a texture unit binding sent from the host application (such as an OpenGL or WebGL API call).

Texture Sampling Syntax

To extract a color value from a samplerCube, GLSL provides built-in texture lookup functions that take the sampler and a normalized 3D direction vector as arguments.

In modern GLSL (version 3.30+ and GLSL ES 3.0+), the overloaded texture function is used:

vec4 color = texture(u_EnvironmentMap, directionVector);

In legacy GLSL (version 1.20 and GLSL ES 2.0), the specific textureCube function is used:

vec4 color = textureCube(u_EnvironmentMap, directionVector);

Environment Mapping Example

For environment reflection mapping, the sampling vector is computed using the surface normal and the incident view direction (the vector from the camera to the fragment):

#version 330 core
out vec4 FragColor;

in vec3 v_Normal;
in vec3 v_Position;

uniform vec3 u_CameraPosition;
uniform samplerCube u_EnvironmentMap;

void main()
{
    // Compute the incident vector from the camera to the surface
    vec3 I = normalize(v_Position - u_CameraPosition);

    // Calculate the reflection vector based on the surface normal
    vec3 R = reflect(I, normalize(v_Normal));

    // Sample the cubemap using the reflected direction vector
    FragColor = vec4(texture(u_EnvironmentMap, R).rgb, 1.0);
}

By substituting reflect() with GLSL's built-in refract() function and supplying an index of refraction ratio, the same samplerCube setup is used to render accurate refraction effects.