Chromatic Aberration Texture Distortion in GLSL
This article explores how chromatic aberration is simulated in real-time computer graphics using GLSL post-processing shaders. It covers the mathematical principles behind lens dispersion, explains how fragment shaders displace red, green, and blue texture coordinates along directional and radial vectors, and provides a direct implementation demonstrating how to achieve optical color fringing in modern rendering pipelines.
The Optical Principle in Shaders
In physical optics, chromatic aberration occurs because a lens has different refractive indices for different wavelengths of light. Shorter wavelengths (blue light) bend more sharply than longer wavelengths (red light), preventing them from focusing at the exact same focal point on an image sensor.
In a GLSL fragment shader, this physical phenomenon is simulated by
modifying normalized texture coordinates (UVs) independently for each
color channel before sampling a 2D texture. Instead of performing a
single texture sample at coordinate uv, the shader
calculates separate coordinates—uvR, uvG, and
uvB—and samples the red, green, and blue components
individually.
Radial Coordinate Distortion
The most physically accurate digital approximation is radial distortion, where color separation increases with the distance from the center of the screen (the optical axis of the lens).
Center Vector Calculation: The fragment shader determines the vector pointing from the center of the screen to the current fragment coordinate: \[\vec{d} = \text{UV} - (0.5, 0.5)\]
Distance Scaling: The displacement magnitude is typically weighted by the distance (\(r = \|\vec{d}\|\)) or the square of the distance (\(r^2\)) to simulate progressive lens distortion toward the edges of the viewport: \[\text{offset} = \vec{d} \cdot r \cdot \text{strength}\]
Coordinate Separation: The color channels are displaced in opposite directions along the radial vector:
- Red Channel: Pushed outward away from the center (\(\text{UV}_R = \text{UV} + \text{offset}\)).
- Green Channel: Kept at the base coordinate or shifted minimally (\(\text{UV}_G = \text{UV}\)).
- Blue Channel: Pulled inward toward the center (\(\text{UV}_B = \text{UV} - \text{offset}\)).
Basic GLSL Implementation
The following fragment shader snippet demonstrates radial chromatic aberration:
#version 330 core
in vec2 v_texCoord;
out vec4 fragColor;
uniform sampler2D u_sceneTexture;
uniform float u_aberrationStrength; // e.g., 0.05
void main()
{
vec2 center = vec2(0.5, 0.5);
vec2 dir = v_texCoord - center;
float dist = length(dir);
// Scale offset quadratically toward screen edges
vec2 offset = dir * (dist * u_aberrationStrength);
// Distort UV coordinates per channel
vec2 uvR = v_texCoord + offset;
vec2 uvG = v_texCoord;
vec2 uvB = v_texCoord - offset;
// Sample channels independently
float r = texture(u_sceneTexture, uvR).r;
float g = texture(u_sceneTexture, uvG).g;
float b = texture(u_sceneTexture, uvB).b;
fragColor = vec4(r, g, b, 1.0);
}Directional and Glitch Distortion
In addition to radial simulation, chromatic aberration can be distorted along fixed screen-space vectors for stylized effects, camera shaking, or digital interference:
- Horizontal/Vertical Shear: Offsets are added along
a single axis (e.g.,
uvR.x += offset). This models prismatic dispersion or digital transmission errors. - Spectrum Sampling: Rather than three discrete samples, high-fidelity implementations take multiple texture samples along the offset vector \(\vec{d}\), mapping each sample to a reconstructed spectral curve. This eliminates sharp three-color halos and produces a smooth rainbow gradient.
Edge Clamping and Artifact Prevention
Distorting texture coordinates can cause coordinates near the
viewport boundary to fall outside the [0.0, 1.0] range. To
prevent visual artifacts such as black borders or texture wrapping:
Set the texture wrap mode in OpenGL to
GL_CLAMP_TO_EDGE.Alternatively, clamp coordinate vectors within GLSL:
uvR = clamp(uvR, 0.0, 1.0); uvB = clamp(uvB, 0.0, 1.0);