Procedural Graphics
Procedural shaders compute images from math alone — the heart of Shadertoy, demoscene, and The Book of Shaders.
19.1 Coordinate setup
vec2 uv = (fragCoord - 0.5 * iResolution.xy) / iResolution.y; // centered, aspect-correct, y in [-0.5, 0.5]19.2 Shaping functions
float y = smoothstep(0.2, 0.8, uv.x); // smooth ramp
float pulse = step(0.4, uv.x) - step(0.6, uv.x); // box
float wave = sin(uv.x * 20.0 + iTime) * 0.5 + 0.5;
float plotLine(vec2 st, float f) { // draw a function graph
return smoothstep(f - 0.01, f, st.y) - smoothstep(f, f + 0.01, st.y);
}
float impulse(float k, float x) { float h = k * x; return h * exp(1.0 - h); } // (Inigo Quilez)
float parabola(float x, float k) { return pow(4.0 * x * (1.0 - x), k); }19.3 2-D signed distance functions (SDFs)
A signed distance function returns the distance to a shape's surface (negative inside).
float sdCircle(vec2 p, float r) { return length(p) - r; }
float sdBox(vec2 p, vec2 b) {
vec2 d = abs(p) - b;
return length(max(d, 0.0)) + min(max(d.x, d.y), 0.0);
}
float sdSegment(vec2 p, vec2 a, vec2 b) {
vec2 pa = p - a, ba = b - a;
float h = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0);
return length(pa - ba * h);
}
float sdRoundedBox(vec2 p, vec2 b, float r) { return sdBox(p, b - r) - r; }
// Boolean operations
float opUnion(float a, float b) { return min(a, b); }
float opIntersect(float a, float b) { return max(a, b); }
float opSubtract(float a, float b) { return max(a, -b); }
float opSmoothUnion(float a, float b, float k) {
float h = clamp(0.5 + 0.5 * (b - a) / k, 0.0, 1.0);
return mix(b, a, h) - k * h * (1.0 - h);
}
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
vec2 p = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
float d = opSmoothUnion(sdCircle(p - vec2(0.15 * sin(iTime), 0.0), 0.15),
sdBox(p + vec2(0.15, 0.0), vec2(0.1)), 0.1);
float aa = fwidth(d);
vec3 col = mix(vec3(1.0, 0.6, 0.2), vec3(0.1), smoothstep(-aa, aa, d)); // fill, anti-aliased
col *= 0.8 + 0.2 *
…19.4 Domain transformations
p = fract(p * 5.0) - 0.5; // repeat space (tiling)
vec2 id = floor(p * 5.0); // cell id for per-cell variation
p = abs(p); // mirror symmetry
p *= mat2(cos(a), -sin(a), sin(a), cos(a)); // rotate
float angle = atan(p.y, p.x); float radius = length(p); // polar coordinates
float sector = mod(angle, 6.2831 / 6.0); // kaleidoscope19.5 Random & noise
Other noises: simplex noise, Worley/cellular (Voronoi) noise, curl noise (flow fields), blue noise (dithering, from textures).
// Hash functions (pseudo-random from coordinates)
float hash21(vec2 p) {
p = fract(p * vec2(123.34, 456.21));
p += dot(p, p + 45.32);
return fract(p.x * p.y);
}
// Value noise
float valueNoise(vec2 p) {
vec2 i = floor(p), f = fract(p);
float a = hash21(i), b = hash21(i + vec2(1, 0)), c = hash21(i + vec2(0, 1)), d = hash21(i + vec2(1, 1));
vec2 u = f * f * (3.0 - 2.0 * f); // smooth interpolation
return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);
}
// Gradient (Perlin-style) noise
vec2 grad(vec2 p) { float a = hash21(p) * 6.2831853; return vec2(cos(a), sin(a)); }
float gradientNoise(vec2 p) {
vec2 i = floor(p), f = fract(p);
vec2 u = f * f * f * (f * (f * 6.0 - 15.0) + 10.0); // quintic fade
return mix(mix(dot(grad(i), f), dot(grad(i + vec2(1, 0)), f - vec2(1, 0)), u.x),
mix(dot(grad(i + vec2(0, 1)), f - vec2(0, 1)), dot(grad(i + vec2(1, 1)), f - vec2(1, 1)), u.x), u.y);
}
// Fractal Brownian Motion (fBm): sum of noise octaves
float fbm(vec2 p) {
float v = 0.0, amp = 0.5;
mat2 rot = mat2(0.8, 0.6, -0.6, 0.8);
for (int i = 0; i < 6; ++i) {
v += amp * gradientNoise(p);
p = rot * p * 2.0;
…// Voronoi (cellular) noise: distance to the nearest feature point
float voronoi(vec2 p) {
vec2 i = floor(p), f = fract(p);
float minD = 1.0;
for (int y = -1; y <= 1; ++y)
for (int x = -1; x <= 1; ++x) {
vec2 cell = vec2(x, y);
vec2 pt = vec2(hash21(i + cell), hash21(i + cell + 17.0));
pt = 0.5 + 0.5 * sin(iTime + 6.2831 * pt); // animated points
minD = min(minD, length(cell + pt - f));
}
return minD;
}19.6 Procedural patterns
Checkerboards (mod(floor(uv.x) + floor(uv.y), 2.0)), stripes, bricks (offset every other row), polka dots, hexagonal grids, truchet tiles, gradients and palettes:
// Inigo Quilez cosine palette: a + b * cos(2π (c t + d))
vec3 palette(float t, vec3 a, vec3 b, vec3 c, vec3 d) { return a + b * cos(6.28318 * (c * t + d)); }
vec3 col = palette(t, vec3(0.5), vec3(0.5), vec3(1.0), vec3(0.0, 0.33, 0.67));