🎨 Visual Computing

Procedural Generative Art: Fragment Shaders, Noise Functions & Raymarching

By Visual Computing & Brand Identity Review Board•

Creative & Technical Insights

Creative visual engineering: Simplex and Perlin noise synthesis, signed distance function (SDF) 3D raymarching, and interactive shader art in WebGL.

The Paradigm of Massive Parallelism in Visual Shaders

Traditional graphic design and digital illustration workflows rely predominantly on explicit geometry: Bézier vectors, path nodes, or polygon wireframe meshes manipulated sequentially on the CPU. While intuitive, geometric representations face severe computational and memory constraints when rendering hyper-detailed organic patterns, fluid dynamics, or recursive topological structures.

Procedural generative art on the GPU fundamentally inverts this paradigm. Rather than calculating geometry and projecting it onto a viewport, modern creative coding employs GLSL fragment shaders running inside WebGL or WebGPU contexts. In a fragment shader, the entire canvas is driven by a full-screen quad (two triangles covering the viewport), and a parallelized micro-program executes simultaneously across millions of GPU compute cores—once for every pixel on screen. Visual form emerges not from discrete stored vertices, but as an analytical solution to mathematical equations evaluated in continuous space.

This architectural shift delivers resolution independence. Because the image is synthesized analytically at the sub-pixel level, an interactive artwork can be displayed on a tiny smartwatch or blown up across an 8K digital cinema installation without experiencing texture stretching or polygon silhouette faceting.

Coordinate Normalization & Aspect Ratio Correction

The foundation of all fragment shader mathematics begins with mapping raw pixel coordinates (gl_FragCoord.xy) into a normalized, resolution-independent Cartesian plane. Naive normalization that simply divides by viewport dimensions ($uv = fragCoord / resolution$) distorts geometric circles into ellipses whenever the display aspect ratio deviates from $1:1$.

Standardized creative shader pipelines center the coordinate origin $(0.0, 0.0)$ at the center of the viewport and scale the coordinates uniformly by the viewport height:

// Standardized GLSL Fragment Shader Boilerplate
#ifdef GL_ES
precision highp float;
#endif

uniform vec2 u_resolution; // Viewport width and height in pixels
uniform float u_time;      // Elapsed time in seconds

void main() {
    // Center the origin and correct for non-square aspect ratios
    vec2 uv = (gl_FragCoord.xy - 0.5 * u_resolution.xy) / u_resolution.y;
    
    // uv.x and uv.y now represent a uniform Cartesian coordinate system
    // uv.y spans [-0.5, +0.5], while uv.x scales with the aspect ratio
    
    vec3 color = vec3(0.0);
    
    // Procedural rendering logic executes here...
    
    gl_FragColor = vec4(color, 1.0);
}

Coherent Noise: Perlin, Simplex & Fractional Brownian Motion (fBm)

Pure pseudorandomness (such as standard hash functions generating white noise) lacks spatial continuity. In organic visual design, nature exhibits smooth, continuous transitions—exemplified by marble grain, terrain contours, atmospheric fog, and water ripples. This requires gradient noise functions.

While Ken Perlin's original 1985 noise function revolutionized computer graphics, its hypercubic grid structure introduces directional artifacts along cardinal axes and scales quadratically in higher dimensions. Simplex noise partitions $N$-dimensional space into regular simplices (equilateral triangles in 2D, tetrahedra in 3D), reducing computational arithmetic while improving isotropic quality.

To simulate complex organic textures, creative shaders stack multiple octaves of noise at escalating spatial frequencies and diminishing amplitudes—a technique known as Fractional Brownian Motion (fBm):

// 2D Fractional Brownian Motion (fBm) Implementation
float hash(vec2 p) {
    p = 50.0 * fract(p * 0.3183099 + vec2(0.71, 0.113));
    return -1.0 + 2.0 * fract(p.x * p.y * (p.x + p.y));
}

float noise(vec2 p) {
    vec2 i = floor(p);
    vec2 f = fract(p);
    
    // Quintic Hermite interpolation curve
    vec2 u = f * f * f * (f * (f * 6.0 - 15.0) + 10.0);
    
    return mix(mix(hash(i + vec2(0.0, 0.0)), 
                   hash(i + vec2(1.0, 0.0)), u.x),
               mix(hash(i + vec2(0.0, 1.0)), 
                   hash(i + vec2(1.0, 1.0)), u.x), u.y);
}

float fbm(vec2 p) {
    float total = 0.0;
    float amplitude = 0.5;
    float frequency = 1.0;
    
    // Accumulate 5 octaves of harmonic frequency
    for (int i = 0; i < 5; i++) {
        total += amplitude * noise(p * frequency);
        frequency *= 2.0;    // Lacunarity
        amplitude *= 0.5;    // Persistence / Gain
    }
    return total;
}

Domain Warping & Fluid Topological Distortion

While standard Fractional Brownian Motion yields convincing clouds and heightmaps, natural phenomena—such as swirling smoke, turbulent river currents, and marbling paper art—exhibit rotational feedback loops. Visual artists simulate this via Domain Warping.

In domain warping, the input coordinate vector $p$ is displaced by another noise evaluation before being passed into the primary noise function: $g(p) = f(p + f(p))$. By cascading multiple tiers of warped coordinates, the shader synthesizes intricate swirling tendrils and fluid filaments with zero computational fluid dynamic (CFD) simulation overhead:

// Two-tier Domain Warping Equation
vec2 warpPattern(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)));
    return r;
}

Signed Distance Functions (SDF) & Constructive Solid Geometry

In analytical procedural graphics, shapes are defined implicitly via Signed Distance Functions (SDFs). An SDF takes a point in space $p$ and returns a scalar value representing the shortest Euclidean distance from $p$ to the nearest boundary of the geometry. If the point lies outside the shape, the distance is positive; if inside, the distance is negative; and if directly on the boundary, the distance is exactly zero.

SDFs are exceptionally powerful because complex geometric compositions can be created using simple Boolean operations evaluated purely through minimum, maximum, and negation arithmetic:

  • Union ($A \cup B$): min(distA, distB)
  • Intersection ($A \cap B$): max(distA, distB)
  • Subtraction ($A \setminus B$): max(distA, -distB)
  • Smooth Minimum (Organic Blending): Polynomial interpolation that welds intersecting primitives with smooth curvature filleting.

Sphere Tracing Raymarching for 3D Procedural Scenes

When extending SDFs into three-dimensional space, shaders employ raymarching (specifically sphere tracing). Unlike rasterizing polygonal meshes, a camera ray is projected from the eye through each screen pixel. Rather than advancing by a constant step size, the algorithm queries the global scene SDF. Because the SDF guarantees that no surface exists within the returned radius, the ray can safely advance forward by that exact distance without risking collision.

// 3D SDF Sphere and Raymarch Loop
float sdSphere(vec3 p, float radius) {
    return length(p) - radius;
}

float sceneSDF(vec3 p) {
    // Evaluate complex composition of 3D primitives
    float sphere1 = sdSphere(p - vec3(0.0, 0.0, 3.0), 1.0);
    return sphere1;
}

vec3 calcNormal(vec3 p) {
    const float eps = 0.001;
    const vec2 h = vec2(eps, 0);
    return normalize(vec3(
        sceneSDF(p + h.xyy) - sceneSDF(p - h.xyy),
        sceneSDF(p + h.yxy) - sceneSDF(p - h.yxy),
        sceneSDF(p + h.yyx) - sceneSDF(p - h.yyx)
    ));
}

vec3 raymarch(vec3 ro, vec3 rd) {
    float depth = 0.0;
    const float maxDist = 50.0;
    const int maxSteps = 100;
    
    for (int i = 0; i < maxSteps; i++) {
        vec3 p = ro + depth * rd;
        float dist = sceneSDF(p);
        
        if (dist < 0.001) {
            // Hit detected: Compute surface normal and Blinn-Phong lighting
            vec3 normal = calcNormal(p);
            vec3 lightDir = normalize(vec3(1.0, 2.0, -1.0));
            float diff = max(dot(normal, lightDir), 0.0);
            return vec3(0.76, 0.25, 0.05) * diff; // Terracotta accent color
        }
        
        depth += dist;
        if (depth >= maxDist) break;
    }
    return vec3(0.05, 0.05, 0.05); // Background void
}

Procedural Ambient Occlusion & Volumetric Lighting

One of the profound visual strengths of Signed Distance Fields in raymarching is the ability to calculate high-fidelity ambient occlusion (AO) at virtually zero performance cost. In traditional rasterization, ambient occlusion requires multi-pass screen-space buffers (SSAO) or heavy ray tracing compute trees.

In an SDF scene, surface ambient occlusion can be estimated by stepping outwards along the surface normal at several discrete increments and evaluating the scene distance function. If nearby geometry crowds the normal ray, the evaluated distance will be smaller than the step distance, indicating geometric shadowing. Multiplying this factor into the diffuse illumination adds deep, physical contact shadows to organic procedural geometries.

Summary of Creative Coding Architectural Advantages

Embracing procedural fragment shaders delivers unparalleled advantages in interactive design systems: infinite spatial scalability without texture asset bloat, procedural animations that never loop or repeat, and real-time interactive parameterization directly driven by browser mouse movements, audio frequencies, or scroll dynamics.

A&K

Visual Computing & Brand Identity Review Board

Our team of creative directors and print specialists establishes benchmarks for color accuracy, custom typography, and physical press craftsmanship.

Commission High-Fidelity Brand Engineering

Collaborate with our atelier to design bespoke brand identity systems, packaging, and custom typography.

Inquire Studio Brief →