Ray Marching and Signed Distance Functions: Procedural 3D Worlds in Shaders
Traditional real-time computer graphics relies on polygon rasterization: dividing 3D models into thousands of triangles and projecting their vertices onto a 2D screen coordinate buffer.
While GPUs excel at rasterizing triangles, mathematical surfaces—such as fractal landscapes, infinite repetitive geometry, smooth organic blending, and volumetrics—are notoriously difficult and memory-intensive to represent as discrete meshes.
Ray Marching with Signed Distance Functions (SDFs) flips this paradigm. Instead of rendering meshes, we describe 3D space purely as mathematical equations evaluated entirely within a single fragment shader!
🎯 1. The Sphere Tracing Algorithm
In standard Ray Tracing, we analytically calculate the exact mathematical intersection point between a camera ray and a geometric primitive (e.g., ray-sphere intersection quadratic formula).
In Ray Marching (Sphere Tracing):
- We define a ray origin $\vec{o}$ and normalized direction $\vec{d}$.
- For any point $\vec{p}$ in 3D space, an SDF returns the shortest Euclidean distance from $\vec{p}$ to the surface of the scene:
* $d(\vec{p}) > 0$: Point is outside the object.
* $d(\vec{p}) = 0$: Point is precisely on the boundary surface.
* $d(\vec{p}) < 0$: Point is inside the object.
- Because $d(\vec{p})$ tells us the radius of a sphere guaranteed to contain no geometry, we can safely step along the ray by exactly $d(\vec{p})$ without skipping any collisions!
float rayMarch(vec3 ro, vec3 rd) {
float dO = 0.0; // Distance from ray origin
for(int i = 0; i < 128; i++) {
vec3 p = ro + rd * dO;
float dS = mapSceneSDF(p);
dO += dS;
if(dS < 0.001 || dO > 100.0) break; // Hit or miss
}
return dO;
}📐 2. Elementary Signed Distance Functions
The mathematical beauty of SDFs is their brevity:
// Sphere: center at origin with radius r
float sdSphere(vec3 p, float r) {
return length(p) - r;
}
// Box: dimensions b (half-extents)
float sdBox(vec3 p, vec3 b) {
vec3 q = abs(p) - b;
return length(max(q, 0.0)) + min(max(q.x, max(q.y, q.z)), 0.0);
}
// Torus: major radius t.x, minor tube radius t.y
float sdTorus(vec3 p, vec2 t) {
vec2 q = vec2(length(p.xz) - t.x, p.y);
return length(q) - t.y;
}🔀 3. Constructive Solid Geometry (CSG) and Smooth Minimum
Combining shapes in polygon modeling requires complex Boolean mesh slicing. In SDFs, it is simple arithmetic:
- Union: $\min(d_1, d_2)$
- Intersection: $\max(d_1, d_2)$
- Subtraction: $\max(-d_1, d_2)$
Smooth Blending (The Magic of $smin$)
By replacing discrete $\min$ with a polynomial smooth minimum, objects fuse organically like drops of liquid mercury:
float smin(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);
}💡 4. Calculating Surface Normals & Shading
How do we shade a surface when there are no polygon normal vectors? We approximate the gradient of the scalar field numerically using central differences:
vec3 calcNormal(vec3 p) {
const float h = 0.0001;
const vec2 k = vec2(1.0, -1.0);
return normalize(
k.xyy * mapSceneSDF(p + k.xyy * h) +
k.yyx * mapSceneSDF(p + k.yyx * h) +
k.yxy * mapSceneSDF(p + k.yxy * h) +
k.xxx * mapSceneSDF(p + k.xxx * h)
);
}Once the normal $\vec{n}$ is obtained, we calculate Phong/Blinn lighting, ambient occlusion, and screen-space soft shadows in just a few lines of GLSL code.
🎓 The Anim Studio Approach at Kone Academy
In Anim Studio, our 3D graphics track trains creators to understand rendering physics from first principles. By mastering shaders and procedural mathematics, artists and engineers build immersive, lightweight WebGL experiences that run at 60 FPS in any browser.

