Smooth Subtraction and Intersection of SDFs in GLSL

This article provides the mathematical formulations and ready-to-use GLSL implementations for smooth subtraction and smooth intersection of Signed Distance Functions (SDFs). Standard Constructive Solid Geometry (CSG) operations produce sharp transitions between intersecting geometries; smooth variants replace the standard non-differentiable min and max functions with continuous polynomial blending functions, enabling organic, seamless transitions controlled by a smoothing radius parameter.

The Foundation: Smooth Minimum and Maximum

Standard CSG operations rely on min(a, b) for union and max(a, b) for intersection and subtraction. Smooth boolean operations substitute these with smooth minimum (smin) and smooth maximum (smax).

The standard polynomial smooth minimum (quadratic) formulated by Inigo Quilez is defined as:

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);
}

Because maximum operations can be expressed through minimum operations via the identity \(\max(a, b) = -\min(-a, -b)\), the smooth maximum is defined mathematically as:

\[\text{smax}(a, b, k) = -\text{smin}(-a, -b, k)\]

In GLSL, smax can be implemented directly to avoid excessive negation:

float smax(float a, float b, float k) {
    float h = clamp(0.5 + 0.5 * (b - a) / k, 0.0, 1.0);
    return mix(a, b, h) + k * h * (1.0 - h);
}

Smooth Intersection

Mathematical Formulation

The standard intersection between two distance fields \(d_1\) and \(d_2\) is the point-wise maximum:

\[\text{Intersection}(d_1, d_2) = \max(d_1, d_2)\]

The smooth intersection replaces the standard maximum with the smooth maximum parameterized by a blend factor \(k > 0\):

\[\text{SmoothIntersection}(d_1, d_2, k) = \text{smax}(d_1, d_2, k) = -\text{smin}(-d_1, -d_2, k)\]

GLSL Implementation

Using the direct polynomial expansion:

float opSmoothIntersection(float d1, float d2, float k) {
    float h = clamp(0.5 - 0.5 * (d2 - d1) / k, 0.0, 1.0);
    return mix(d2, d1, h) + k * h * (1.0 - h);
}

Alternatively, utilizing an existing smin function:

float opSmoothIntersection(float d1, float d2, float k) {
    return -smin(-d1, -d2, k);
}

Smooth Subtraction (Difference)

Mathematical Formulation

Standard subtraction of shape \(d_1\) from shape \(d_2\) is defined by intersecting the inversion of \(d_1\) with \(d_2\):

\[\text{Subtraction}(d_1, d_2) = \max(-d_1, d_2)\]

Applying smooth maximum yields:

\[\text{SmoothSubtraction}(d_1, d_2, k) = \text{smax}(-d_1, d_2, k)\]

Substituting into the equivalence \(\text{smax}(a, b, k) = -\text{smin}(-a, -b, k)\):

\[\text{SmoothSubtraction}(d_1, d_2, k) = -\text{smin}(d_1, -d_2, k)\]

GLSL Implementation

Using direct formulation where shape d1 is carved out of shape d2:

float opSmoothSubtraction(float d1, float d2, float k) {
    float h = clamp(0.5 - 0.5 * (d2 + d1) / k, 0.0, 1.0);
    return mix(d2, -d1, h) + k * h * (1.0 - h);
}

Alternatively, calling smin directly:

float opSmoothSubtraction(float d1, float d2, float k) {
    return -smin(d1, -d2, k);
}

Behavior of the Smoothing Factor \(k\)

  • When \(k \to 0\), both operations strictly converge to their standard counterparts: max(d1, d2) and max(-d1, d2).
  • When \(|d_1 - d_2| \ge k\) (for intersection) or \(|(-d_1) - d_2| \ge k\) (for subtraction), the polynomial factor \(h\) clamps to 0.0 or 1.0, ensuring the operations remain strictly local without affecting distant geometry.