Skip to main content
GameDev.net gamedev.net

Combining Distance Fields

by GameDev.net · GLSL ES 3.00 (WebGL2) · 25 Aug 2026

Use in your engine

Run the shader to adjust these controls.

What it demonstrates

The reason distance fields matter is that they compose. Combining two shapes needs no geometry, no clipping and no stencil buffer: union is a minimum, intersection is a maximum, and subtraction is a maximum against a negated field. This shader applies all three to the same box and circle so the operator, not the shapes, is what changes.

Source Revision 1

Author notes are linked to specific lines.

Shader inputs

void mainImage(out vec4 fragColor, in vec2 fragCoord)

Called once per pixel. Write the colour to fragColor.

iResolution vec3
Viewport size in pixels (z is the pixel aspect ratio).
iTime float
Seconds since the shader started.
iTimeDelta float
Seconds since the previous frame.
iFrameRate float
Frames per second, smoothed.
iFrame int
Frames rendered since the start.
iMouse vec4
Mouse position: xy while held, zw of the last click.
iDate vec4
Year, month, day, and seconds within the day.
iChannel0 sampler2D
Texture bound to channel 0.
iChannel1 sampler2D
Texture bound to channel 1.
iChannel2 sampler2D
Texture bound to channel 2.
iChannel3 sampler2D
Texture bound to channel 3.
iChannelResolution vec3[4]
Pixel size of each bound channel texture.
iChannelTime float[4]
Playback time of each channel, in seconds.
iSampleRate float
Audio sample rate, always 44100.
Main Image6
1 uniform int uOperation; // @param 0..2 = 2 "Operation"
2 uniform float uSeparation; // @param 0.0..0.40 = 0.14 "Separation"
3 uniform float uSize; // @param 0.10..0.34 = 0.22 "Shape size"
4
5 float sdCircle(vec2 p, float radius) {
6 return length(p) - radius;
7 }
8
9 float sdBox(vec2 p, vec2 halfSize) {
10 vec2 d = abs(p) - halfSize;
11 return length(max(d, 0.0)) + min(max(d.x, d.y), 0.0);
12 }
13
14 float opUnion(float a, float b) {
15 return min(a, b);
16 }
17
18 float opIntersect(float a, float b) {
19 return max(a, b);
20 }
21
22 float opSubtract(float a, float b) {
23 return max(a, -b);
24 }
25
26 vec3 shadeField(float d) {
27 vec3 color = (d < 0.0) ? vec3(0.18, 0.42, 0.72) : vec3(0.96, 0.74, 0.34);
28 color *= 1.0 - exp(-7.0 * abs(d));
29 color *= 0.88 + 0.12 * cos(140.0 * d);
30 return mix(color, vec3(1.0), 1.0 - smoothstep(0.0, 0.006, abs(d)));
31 }
32
33 void mainImage(out vec4 fragColor, in vec2 fragCoord) {
34 vec2 p = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
35 float box = sdBox(p + vec2(uSeparation, 0.0), vec2(uSize * 0.95));
36 float circle = sdCircle(p - vec2(uSeparation, 0.0), uSize);
37 float d;
38 if (uOperation == 0) {
39 d = opUnion(box, circle);
40 } else if (uOperation == 1) {
41 d = opIntersect(box, circle);
42 } else {
43 d = opSubtract(box, circle);
44 }
45 fragColor = vec4(shadeField(d), 1.0);
46 }
47

Learn from this shader

How it works

Each shape is evaluated at the same point, offset in opposite directions so Separation moves them through each other. Union takes the smaller distance, which is correct because the nearest surface is the nearer of the two. Intersection takes the larger, which is exactly right only inside the overlap and overestimates outside it — the reason intersections can look subtly wrong under lighting. Subtraction negates the second field, turning its inside into outside, and intersects with that. The visualization keeps the contour bands so you see the operator acting on the whole field, not just the silhouette.

Try changing

Step Operation with Separation held part way so the shapes overlap, and watch the bands break at the seams where the minimum or maximum switches which shape wins. Notice the sharp crease every operator leaves: that discontinuity in the field is exactly what the next lesson smooths away. Drive Separation to zero and the subtraction hollows the box out completely.

Using it in a game

This is how procedural geometry, destructible holes, damage cavities and boolean level shapes are expressed in raymarched or distance based renderers. The caveat is the approximation: a union is exact, but an intersection or subtraction field is not a true distance any more, so cone marching and thick outline effects need extra care once you have used them.

Explore the techniques

Continue with curated explanations and progressively related examples.

LicenseMIT
Views0
Forks0

Discussion

Loading comments...