Domain Warping in GLSL for Procedural Terrain
Domain warping is a procedural generation technique that distorts coordinate spaces using noise functions, enabling the creation of organic, turbulent topographies. In GLSL, applying domain warping to procedural heightmaps disrupts the uniform regularity of standard Perlin or Simplex noise, replacing predictable rolling hills with chaotic, fluid-like structures such as twisted canyons, sharp ridges, and stratified mountain ranges. This guide breaks down the core mathematics of domain warping, demonstrates its implementation using GLSL shaders, and explains how chaining noise layers simulates complex geological turbulence.
The Mechanics of Domain Warping
Standard procedural terrain calculates an elevation \(h\) by feeding spatial coordinates directly into a noise algorithm:
\[h = \text{noise}(p)\]
Where \(p\) represents a
two-dimensional or three-dimensional position vector (vec2
or vec3). While effective for basic undulating landscapes,
this approach produces predictable patterns that lack the dynamic
shearing and directional flow seen in natural geology.
Domain warping alters this pipeline by modifying the coordinate space before evaluating the final noise function. By sampling an initial noise function to offset the coordinates, the domain itself is deformed:
\[p' = p + \alpha \cdot \vec{g}(p)\] \[h = \text{noise}(p')\]
In this equation, \(\vec{g}(p)\) is a vector-valued noise function (often derived from multiple offset noise samples), and \(\alpha\) is a scaling factor controlling the intensity of the warp. The resulting sample pulls and compresses the underlying coordinate grid, creating shearing effects and swirling, turbulent behavior.
GLSL Implementation
In a GLSL fragment or compute shader, domain warping is typically combined with Fractional Brownian Motion (fBm) to generate multi-octave detail. Rather than using raw noise, using fBm produces continuous, natural-looking variance across different scales.
// Assumes a standard 2D noise function: float noise(vec2 p);
// Fractional Brownian Motion to aggregate noise octaves
float fbm(vec2 p) {
float value = 0.0;
float amplitude = 0.5;
float frequency = 1.0;
for (int i = 0; i < 5; i++) {
value += amplitude * noise(p * frequency);
frequency *= 2.0;
amplitude *= 0.5;
}
return value;
}
// Single-layer domain warped terrain
float terrainSimpleWarp(vec2 p) {
vec2 distortion = vec2(
fbm(p + vec2(0.0, 0.0)),
fbm(p + vec2(5.2, 1.3)) // Offset to decorrelate axes
);
return fbm(p + 4.0 * distortion);
}Cascading Warp for Extreme Turbulence
To achieve advanced geological turbulence, you can chain multiple domain warps together. By feeding the distorted coordinates of one layer into another, the terrain develops sharp folds, overhang-like appearances, and complex vortex patterns reminiscent of fluid dynamics and tectonic deformation.
In GLSL, a two-tier nested warp can be written as follows:
float terrainTurbulentWarp(vec2 p) {
vec2 q = vec2(
fbm(p + vec2(0.0, 0.0)),
fbm(p + vec2(5.2, 1.3))
);
vec2 r = vec2(
fbm(p + 4.0 * q + vec2(1.7, 9.2)),
fbm(p + 4.0 * q + vec2(8.3, 2.8))
);
// Final height calculation using the nested coordinate space
return fbm(p + 4.0 * r);
}In this setup:
- Vector
qapplies an initial wave of deformation across the base coordinates. - Vector
rsamples the domain already shifted byq, twisting the coordinates a second time. - The final elevation uses
rto index into the primary height function, compounding the structural turbulence.
Tuning Terrain Characteristics
Controlling the look of the generated terrain depends on three primary variables:
- Displacement Amplitude: Modifying the multiplier
applied to the offset vectors (
qandr) dictates how violently the space is stretched. High values yield wispy, marbled, and violently folded terrain, while low values produce subtle, soft variations. - Vector Offsets: Adding static offsets (such as
vec2(1.7, 9.2)) inside the intermediate fBm calls prevents the \(x\) and \(y\) distortions from mirroring each other diagonally, ensuring asymmetric, natural terrain formations. - Octave Reduction on Warping Layers: Evaluating
high-octave fBm for every warp stage is computationally expensive.
Running the initial distortion vectors (
qandr) with fewer octaves (e.g., 2 to 3 iterations) and reserving higher octaves (5 to 8 iterations) for the final height sample maximizes GPU performance while retaining sharp, high-frequency terrain features.