On Signed Distance Functions

On Signed Distance Functions

Aug 11, 2020
SDF, mathematics

Reading time: 12 minutes and 41 seconds.

Introduction

Formal Definition

In more rigorous terms, signed distance functions (aka SDFs) are defined as functions that satisfy a particular form of the eikonal equation1. The general eikonal equation is of the form:

$$||\nabla u(x)|| = \frac{1}{f(x)},\quad x \in \Omega,$$

where \(\Omega\) is an open set in \(\mathbb{R}^n\), and \(f(x) : \mathbb{R}^n \to \mathbb{R}\) is a function with positive values. In physical terms, the solution \(u(x)\) to this nonlinear partial differential equation can be interpreted as the shortest time needed to travel from the boundary \(\partial\Omega\) to \(x \in \Omega\), with \(f(x)\) being the speed at \(x\).

Now, for a signed distance function, the speed will be unity throughout the domain, and \(x\) will not be constrained to lie in \(\Omega\). With \(f(x) \equiv 1\), the time to travel from the boundary \(\partial\Omega\) to any \(x \in \mathbb{R}^n\) will simply be equal to the shortest distance from \(x\) to \(\partial\Omega\). We may then obtain a signed distance function \(\textsf{sdf}(x)\), provided that the following conditions are satisfied:

  1. Eikonal equation: $$\tag{EE} ||\nabla\textsf{sdf}(x)|| = 1,\quad x \in \mathbb{R}^3,$$
  2. Zero distance at the boundary: $$\tag{BZ} \textsf{sdf}(x) = 0,\quad \forall \ x \in \partial\Omega,$$
  3. Inward-facing normal at the boundary: $$\tag{IN} \nabla\textsf{sdf}(x) = N(x),\quad \forall \ x \in \partial\Omega.$$

If these conditions are satisfied, a function representing the shortest Euclidean distance from \(x \in \mathbb{R}^n\) to a surface \(\partial\Omega \subset \Omega \subset \mathbb{R}^n\) is obtained. This distance is signed, meaning that when \(x \in \Omega\), \(\textsf{sdf}(x)\) is nonnegative (\(\geq 0\)), while for \(x \in \mathbb{R}^n \setminus \Omega\), \(\textsf{sdf}(x)\) it is negative (\(< 0\)).

To prove that these conditions produce a distance field (disregarding the sign for now), consider points \(x, y\) on a path of steepest-descent, that is a path along gradient \(\nabla \textsf{sdf}\). As established previously \(||\nabla \textsf{sdf}(x)|| = 1\) (condition (EE)). The Euclidean distance between these two points will be at most equal to the length of the steepest-descent path, denoted here by \(\gamma\). We find that:

$$ \tag{G} ||x - y|| \leq |\gamma| = \left| \int_\gamma \nabla \textsf{sdf}(\xi) \cdot \mathrm{d}s \right| = |\textsf{sdf}(x) - \textsf{sdf}(y)|. $$

Then, considering a straight line path from \(x\) to \(y\), i.e., the shortest path between the two points, denoted here by \(\zeta\), we find the following bound:

$$ \tag{Z} \begin{aligned} ||x - y|| &= |\zeta| = \int_\zeta ||\nabla \textsf{sdf}(\xi)|| \ ||\mathrm{d}s|| \cr &\geq \left| \int_\zeta \nabla \textsf{sdf}(\xi) \cdot \mathrm{d}s \right| = |\textsf{sdf}(x) - \textsf{sdf}(y)|. \end{aligned} $$

The latter inequality is proven straightforwardly by application of the Cauchy–Schwartz inequality.

Combining inequalities (G) and (Z), we find that the following must hold:

$$ ||x - y|| = |\textsf{sdf}(x) - \textsf{sdf}(y)|. $$

Finally, from the boundary conditions ((BZ) and (IN)), it is ensured that the function will in fact be a signed distance function for a given object \(\Omega\).

Differentiability

Since for certain geometries, the derivative at a point where there exist multiple minima (e.g., along the diagonal of a rectangle), is not defined. The points at which this is true are interesting in their own right; as a matter of fact, this set of points is known as the medial axis, or when this set is closed, the cut locus.

In the box below, an example of this phenomenon is given for a 2D square SDF.

Boolean Operations (Combinations)

In order to create more complex shapes, it is reasonable to want to run combination operatons on primitives. To get a better handle on what these operations might be, consider the following basic examples:

  • Union
  • Difference
  • Intersection

Below, we consider these, and other, operations on two shapes (sets) \(P\) and \(Q\), resulting in a set \(R\). The SDFs belonging to these sets will be subscripted by the name of the set to which they belong. For more information on this topic, see 2, 3.

Union

$$ R = P \cup Q \Leftrightarrow \textsf{sdf}_R (p) = \min[\textsf{sdf}_P (p), \textsf{sdf}_Q (p)]. $$

Intersection

$$ R = P \cap Q \Leftrightarrow \textsf{sdf}_R (p) = \max[\textsf{sdf}_P (p), \textsf{sdf}_Q (p)]. $$

Note: This does not produce a proper SDF on the interior.

Difference

$$ R = P \setminus Q \Leftrightarrow \textsf{sdf}_R (p) = \max[\textsf{sdf}_P (p), -\textsf{sdf}_Q (p)]. $$

Note: This does not produce a proper SDF on the interior.

Symmetric Difference

Also known as the disjunctive union. Expressed in words, this is the union of the difference of the two sets.

$$ R = P \oplus Q = (P \setminus Q) \cup (Q \setminus P) $$ $$\Leftrightarrow$$ $$ \textsf{sdf}_R (p) = \min\{\max[\textsf{sdf}_P (p), -\textsf{sdf}_Q (p)], \max[\textsf{sdf}_Q (p), -\textsf{sdf}_P (p)]\}. $$

Note: This does not produce a proper SDF on the interior.

Complement

The complement of an SDF is simply its negative, since its interior then becomes its exterior.

$$ R = P^\complement \Leftrightarrow \textsf{sdf}_R (p) = -\textsf{sdf}_P (p). $$

Transformations

Translation

Consider a translation by distance \(t \in \mathbb{R}^3\). The translated SDF will be of the form:

$$ \textsf{sdf}_R (p) = \textsf{sdf}_P (p - t). $$

Scaling

We distinguish uniform and nonuniform scaling. In the first case, the scaling factor is the same across all axes, while in the latter case different scaling factors may be used on different axes.

Uniform Scaling

For uniform scaling, consider a scaling factor \(s \in \mathbb{R}_+\), i.e, the positive orthant of \(\mathbb{R}\). It can be shown that (see ‘Derivations’ below):

$$ \textsf{sdf}_R (p) = \textsf{sdf}_P (p/s) s . $$

Nonuniform Scaling

As discussed in the ‘Derivation’ box below, there is no straightforward solution to the nonuniform scaling problem that does not violate the eikonal equation condition. The next best ‘solution’, would be to obtain some sort of lower distance bound. This provides a straightforward approach to obtaining a lower bound to the scale surface, which will appear correctly when rendered, but does not have a proper SDF. More advantages are listed in the ‘Derivation’ box below. For a similar discussion, see 4.

Here, we consider a scaling factor \(s \in \mathbb{R}_+^3\). As a lower bound, we obtain:

$$ \textsf{sdf}_{s,\text{min}} (p) = \textsf{sdf} (p \oslash s) s_\text{min}, $$

where \(s_\text{min}\) is the smallest element of \(s\), and ‘\(\oslash\)’ denotes componentwise division.

List of 2D Functions

The following signed distance functions assume that the primitive is centered at the origin, unless stated otherwise. Most of these functions are adapted from Inigo Quilez’s excellent 2D SDF functions article, while others are derived from other sources in the literature.

Point

$$ \textsf{sdf}(p) = ||p|| = \sqrt{p_x^2 + p_y^2}. $$

$$ $$

Implementation

float point(vec2 p)
{
    return length(p);
}
static public inline function point(p:Vec2):FFloat {
    return p.length;
}

Circle

A circle simply offsets the distance to a point by a radius \(r\), as follows:

$$ \textsf{sdf}(p, r) = ||p|| - r = \sqrt{p_x^2 + p_y^2} - r. $$

Implementation

float circle(vec2 p, float r)
{
    return length(p) - r;
}
static public inline function circle(p:Vec2, r:FFloat):FFloat {
    return p.length - r;
}

Rectangle

For a rectangle, consider a 2D vector \(d\) consisting of the width (in \(x\)-direction), and the height. Operators with the subscript \(i\) notation (e.g., \(\textrm{abs}_i\)) will act on a vector in a component-wise fashion (similar to Einstein’s tensor notation); see 5 for more information.

$$\begin{aligned} d(b) &= \textrm{abs}_i(p)-b, \cr \textsf{sdf}(p, d(b)) &= ||\max_i(d, 0)|| + \min(\max(d_x, d_y), 0). \end{aligned}$$

Implementation

float rectangle(vec2 p, vec2 b)
{
    vec2 d = abs(p)-b;
    return length(max(d,0.0)) + min(max(d.x,d.y),0.0);
}
static public function rectangle(p:Vec2, b:Vec2):FFloat {
    final d = p.abs().sub(b);
    return d.max(0).length + Math.min(Math.max(d.x, d.y), 0);
}

Rounded Rectangle

The rounded rectangle follows a similar construction method to the rectangle, although the individual corners are rounded independently, producing a different result compared to the rectangle with uniform rounding applied.


  1. Wikipedia. Eikonal equation. ↩︎

  2. Efi Fogel, Ron Wein, Baruch Zukerman, and Dan Halperin (2005). Boolean Set-Operations on \(X\)-monotone Curve Bounded Point-Sets. ↩︎

  3. CGAL, 2D Regularized Boolean Set-Operations. ↩︎

  4. Jamie Wong (2016). Ray Marching and Signed Distance Functions. ↩︎

  5. Wikipedia. Pointwise: Componentwise operations ↩︎