SDF Infinite Repetition Using Modulo in GLSL
This article explains how to achieve infinite spatial repetition of 3D geometry in GLSL raymarching by modifying coordinate space using the modulo operator. By mapping an infinite coordinate space into a recurring periodic domain, a single Signed Distance Field (SDF) evaluation can render an endless grid of duplicated objects without consuming additional computational memory.
The Principle of Domain Repetition
In raymarching, objects are defined by Signed Distance Functions that take a point \(p\) in space and return the shortest distance to the surface of the geometry. Rather than instantiating multiple objects, spatial repetition manipulates the input coordinates before passing them into the SDF.
When you apply a periodic function like modulo to the coordinate vector, space is effectively folded into repeating cells. The SDF evaluates the folded coordinate as if it were a single instance located at the origin of that local cell, causing the raymarcher to render the shape infinitely along the chosen axes.
Implementing Modulo in GLSL
In GLSL, the built-in mod(x, y) function computes \(x - y \cdot \lfloor x / y \rfloor\). If you
directly compute mod(p, c) for a cell size c,
the coordinates in each cell range from 0.0 to
c. Because standard SDF primitives are centered at the
origin (0, 0, 0), placing the domain bounds between
0.0 to c cuts the primitive across the cell
boundaries.
To keep the primitive centered within each repeated cell, the domain
must be offset so that coordinates range from -c / 2 to
+c / 2.
The standard GLSL pattern for symmetric repetition is:
vec3 opRepetition(vec3 p, vec3 spacing) {
return mod(p + 0.5 * spacing, spacing) - 0.5 * spacing;
}Breakdown of the Formula:
- Offsetting the domain
(
p + 0.5 * spacing): Shifts space forward by half a cell so that the origin aligns with the center of the modulo interval. - Applying
mod(..., spacing): Wraps the coordinates periodically within the interval[0, spacing]. - Centering the local coordinate
(
- 0.5 * spacing): Shifts the coordinate back to the range[-0.5 * spacing, 0.5 * spacing], placing(0, 0, 0)directly in the middle of each repeated volume.
Integrating with an SDF
To render an infinite grid of spheres with a radius of
0.2 spaced 1.0 unit apart, the transformed
point is passed directly into the sphere distance function:
float sdSphere(vec3 p, float s) {
return length(p) - s;
}
float map(vec3 p) {
vec3 spacing = vec3(1.0, 1.0, 1.0);
vec3 q = opRepetition(p, spacing);
return sdSphere(q, 0.2);
}You can selectively repeat across fewer dimensions by applying the
operation only to specific components of the vector. For example,
modifying only p.xz creates a repeating plane of objects
while leaving the vertical y axis unchanged.
Limitations and Edge Cases
While modulo repetition is computationally efficient, it introduces specific constraints:
- Cell Overlap: If the radius or bounds of the
geometry exceed
0.5 * spacing, the shape crosses cell boundaries. This leads to harsh geometric clipping and distance field discontinuities, causing raymarching artifacts. - Negative Coordinates: GLSL's
mod(x, y)is defined asx - y * floor(x / y), which behaves consistently across negative and positive numbers. However, custom modulo implementations or alternative language ports must ensure negative coordinates wrap continuously through zero without creating an asymmetric seam. - Index Tracking: Simple modulo repetition discards
the unique ID of each cell. If individual instances require variations
in color, rotation, or scale, you must compute the cell index (e.g.,
using
floor((p + 0.5 * spacing) / spacing)) before wrapping the coordinates.