Geometry & 3D Vision

Signed Distance Function

A scalar field giving the distance to the nearest surface point, negative inside and positive outside, whose zero level set is the surface and whose gradient is the surface normal.

intermediate

A signed distance function (SDF) stores a shape as a scalar field: at every point, the distance to the nearest surface point, signed to say inside or outside, so that the surface is where the field is zero. Among the 3D representations, it answers “how far is the surface, and in which direction?” with one evaluation, which is why it underlies sphere-traced rendering, volumetric depth fusion, collision checking and many neural shape models.

Definition

For a closed surface S\mathcal{S} bounding a solid, the signed distance function gives, at each point, the Euclidean distance to the nearest point of S\mathcal{S}, negative inside the solid, positive outside, and zero on the surface. Some systems flip the sign, so check it when mixing code or data.

Intuition

A depth map stores one distance per pixel, along its ray. An SDF stores, at every point, the radius of the largest ball centred there that touches the surface without crossing it. Walking out from the centre of a ball of radius rr, the value rises by exactly one unit per unit moved, from −r-r through 00 at the surface.

Formal Definition

Let Ω⊂R3\Omega \subset \mathbb{R}^3 be the solid and S=∂Ω\mathcal{S} = \partial\Omega its surface. With d(x,S)=min⁡y∈S∥x−y∥d(\mathbf{x}, \mathcal{S}) = \min_{\mathbf{y} \in \mathcal{S}} \lVert \mathbf{x} - \mathbf{y} \rVert,

f(x)={−d(x,S)x∈Ω,−d(x,S)otherwise,S={x:f(x)=0}.f(\mathbf{x}) = \begin{cases} -d(\mathbf{x}, \mathcal{S}) & \mathbf{x} \in \Omega, \\ \phantom{-}d(\mathbf{x}, \mathcal{S}) & \text{otherwise}, \end{cases} \qquad \mathcal{S} = \{\mathbf{x} : f(\mathbf{x}) = 0\}.

Hart (1996, §2.1) uses the same definition and sign.

Eikonal property. ff is 1-Lipschitz, ∣f(x)−f(y)∣≤∥x−y∥|f(\mathbf{x}) - f(\mathbf{y})| \le \lVert \mathbf{x} - \mathbf{y} \rVert, and wherever it is differentiable,

∥∇f(x)∥=1.\lVert \nabla f(\mathbf{x}) \rVert = 1 .

The distance is the viscosity solution of this eikonal equation with f=0f = 0 on the surface. The gradient fails to exist on the medial axis, the points with two or more nearest surface points, such as the centre of a ball or the diagonal planes inside a box; this set has zero volume, so the property holds almost everywhere (a standard result, stated without a source).

Normal and closest point. Off the medial axis, ∇f(x)\nabla f(\mathbf{x}) is the unit vector along the line from the nearest surface point, pointing outward: away from that point outside the solid, toward it inside. Hart (1996) uses this property outside a convex solid in the proof of Theorem 4. On the surface it is the outward unit normal, and any nearby point projects onto the surface by

xS=x−f(x) ∇f(x).\mathbf{x}_{\mathcal{S}} = \mathbf{x} - f(\mathbf{x})\, \nabla f(\mathbf{x}).

Properties

Exact distances and distance bounds

Hart (1996, §2.1) calls a function a signed distance bound when ∣f(x)∣≤d(x,f−1(0))|f(\mathbf{x})| \le d(\mathbf{x}, f^{-1}(0)): right sign and zero set, but possibly an underestimate. A bound suffices wherever only a ball of free space is needed, as in sphere tracing, and by Hart’s Theorem 1, f/λf / \lambda is a bound for any ff with Lipschitz constant λ\lambda.

SDFs combine into constructive solid geometry (CSG) shapes by Boolean operations (Hart, 1996, §2.4), but the results are not all exact:

fA∪B=min⁡(fA,fB),fA∩B=max⁡(fA,fB),fAˉ=−fA,fA∖B=max⁡(fA,−fB).f_{A \cup B} = \min(f_A, f_B), \qquad f_{A \cap B} = \max(f_A, f_B), \qquad f_{\bar A} = -f_A, \qquad f_{A \setminus B} = \max(f_A, -f_B).

Hart proves that max⁡\max bounds the distance to the intersection (Theorem 3), so subtraction, an intersection with a complement, gives only a bound; his Eq. 15 and Table 1 present min⁡\min as the distance to the union. Each is exact on one side only: max⁡\max inside the intersection, while outside it the nearest point of the intersection can be farther than either surface; min⁡\min outside the union, while inside it the nearest surface point of one shape may lie buried in the other (reasoning from the definitions; the example measures both). Hart’s Table 1 also lists blends, tapers and twists among the operations that give bounds.

Unsigned distance and occupancy

The unsigned distance ∣f∣|f| has the same zero set but no inside, and a V-shaped, non-differentiable minimum at the surface. Hoppe et al. (1992, §3.1) chose a signed field for this reason: zero is a regular value of ff but not of ∣f∣|f|, so the zero set of ff is a manifold. Unsigned fields suit open surfaces, which have no inside.

Occupancy o(x)=[f(x)<0]o(\mathbf{x}) = [f(\mathbf{x}) < 0] keeps only the sign, so it supports neither safe ray steps nor sub-voxel surface placement.

Truncation

A truncated SDF (TSDF) clips it to [−μ,μ][-\mu, \mu], often divided by μ\mu, where μ\mu is the truncation distance. Curless and Levoy (1996) restricted their signed distances and weights to the vicinity of the range measurements, so that surfaces on opposite sides of an object do not interfere and the volume stays compact and cheap to update. A TSDF keeps the zero set but loses the distance property beyond μ\mu: its gradient vanishes there, and a sphere tracer can advance at most μ\mu per step. The TSDF fusion article covers fusing depth maps into one.

Computing an SDF

Analytic primitives. Many shapes have closed-form SDFs; Hart (1996, Appendix A) derives several. For a sphere, f(p)=∥p−c∥−rf(\mathbf{p}) = \lVert \mathbf{p} - \mathbf{c} \rVert - r. For an axis-aligned box with centre c\mathbf{c} and half-sizes h\mathbf{h}, with q=∣p−c∣−h\mathbf{q} = |\mathbf{p} - \mathbf{c}| - \mathbf{h} taken per axis,

f(p)=∥max⁡(q,0)∥+min⁡(max⁡iqi, 0).f(\mathbf{p}) = \lVert \max(\mathbf{q}, \mathbf{0}) \rVert + \min\big(\max_i q_i,\ 0\big).

The first term is the distance from outside to the nearest face, edge or corner, the second the negative distance to the nearest face from inside (our derivation, checked against brute force in the example).

From a mesh. The unsigned distance is the distance to the nearest triangle, found with a spatial index; the sign needs an inside test. Ray parity, an odd number of crossings meaning inside, works on clean watertight meshes but fails when the ray hits an edge or the mesh has holes. The generalized winding number of Jacobson et al. (2013), w(p)=∑fΩf(p)/4πw(\mathbf{p}) = \sum_f \Omega_f(\mathbf{p}) / 4\pi, sums the signed solid angles the triangles subtend at the point; it is 1 inside and 0 outside a closed, consistently oriented mesh and degrades smoothly with holes and non-manifold parts, so thresholding it at 12\tfrac12 gives a sign (the authors themselves go further, with a graph cut). Park et al. (2019, §5) note that human-designed meshes are commonly not watertight and contain internal structures; they computed signed distances from surface points and normals seen by virtual cameras around each object.

From points. Hoppe et al. (1992) fitted a consistently oriented tangent plane (centre oi\mathbf{o}_i, normal n^i\hat{\mathbf{n}}_i) to each point’s neighbours, as described in the point cloud article, and defined f(p)=(p−oi)⋅n^if(\mathbf{p}) = (\mathbf{p} - \mathbf{o}_i) \cdot \hat{\mathbf{n}}_i for the plane whose centre is closest to p\mathbf{p}. Where the projection of p\mathbf{p} falls farther from the data than the sampling density and noise allow, ff is undefined, which preserves boundaries.

On grids. Given distances in a thin band of voxels around the surface, the rest of a grid is filled by solving the eikonal equation outward: the fast marching method (Sethian, 1996) advances the front monotonically, and the fast sweeping method (Zhao, 2005) runs Gauss–Seidel sweeps in alternating directions, at O(N)O(N) cost for NN grid points with 2n2^n sweeps sufficient for the distance in nn dimensions, according to its abstract.

Using an SDF

Sphere tracing. To find where a ray r(t)=o+td\mathbf{r}(t) = \mathbf{o} + t\mathbf{d} first meets the surface, step by the field’s value, ti+1=ti+f(r(ti))t_{i+1} = t_i + f(\mathbf{r}(t_i)), starting from t0=0t_0 = 0. The ball of radius ∣f∣|f| around the current point contains no surface, so no step overshoots; Hart (1996, §2.2) calls these unbounding spheres, and the method needs only a distance bound, not a derivative. Hart proves linear convergence to the first root (Theorem 2), notes that a ray parallel to a ground plane steps at fixed intervals without ever converging (§2.5.5), and found on a sphere that halving the distance estimate doubled the number of steps (§4.3).

Inside and collision tests. The sign is an inside test and the value a margin: a ball of radius rr centred at c\mathbf{c} is collision-free when f(c)>rf(\mathbf{c}) > r, and a penetrating point is pushed out by −f(x)-f(\mathbf{x}) along ∇f(x)\nabla f(\mathbf{x}) (reasoning from the definition), which motion planning and physics simulation exploit.

Offsets and morphology. {f≤r}\{f \le r\} is the solid dilated by a ball of radius rr and {f≤−r}\{f \le -r\} the solid eroded by it, continuous counterparts of the operators of mathematical morphology. The shifted field f−rf - r is exact outside the dilated solid but in general only a bound inside it, so an opening (erosion, then dilation) needs the distance recomputed in between (reasoning from the definitions).

Surface extraction. On a grid, marching cubes extracts the zero level set as a triangle mesh, interpolating along voxel edges to place vertices inside cells.

Neural SDFs. DeepSDF (Park et al., 2019) regresses the signed distance, with this sign convention, from a network conditioned on a latent code per shape. Its loss clamps prediction and target to [−δ,δ][-\delta, \delta] with δ=0.1\delta = 0.1; a larger δ\delta allows faster ray tracing, a smaller one concentrates capacity near the surface (§3), the trade-off of TSDF truncation. The neural implicit representations article covers neural fields.

Examples

The NumPy and SciPy code combines a sphere and an overlapping box into a union, an intersection and the box minus the sphere. On a 48348^3 grid over [−1,1]3[-1, 1]^3 it checks each field two ways: the eikonal test (finite-difference ∥∇f∥\lVert \nabla f \rVert more than 0.05 from 1) and brute force (the distance to 400,000 surface samples, signed by the exact inside test; sampling error up to about 0.004). It then sphere-traces a 32×2432 \times 24 depth image from a camera with the world-to-camera convention Xc=RXw+tX_c = R X_w + t of the camera parameters article, compares each depth ZZ with exact ray–primitive intersections, and builds a TSDF of the union.

import numpy as np
from scipy.spatial import cKDTree

# Two primitives, negative inside, positive outside, zero on the surface.
C_S, R_S = np.array([0.25, 0.0, 0.0]), 0.5          # sphere: centre, radius
C_B, H_B = np.array([-0.2, 0.0, 0.0]), np.array([0.4, 0.3, 0.3])  # box: centre, half-sizes

def sphere(p):
    return np.linalg.norm(p - C_S, axis=-1) - R_S

def box(p):
    q = np.abs(p - C_B) - H_B                         # per-axis distance past each face
    outside = np.linalg.norm(np.maximum(q, 0.0), axis=-1)
    inside = np.minimum(q.max(axis=-1), 0.0)
    return outside + inside

shapes = {
    "sphere":       sphere,
    "box":          box,
    "union":        lambda p: np.minimum(sphere(p), box(p)),
    "intersection": lambda p: np.maximum(sphere(p), box(p)),
    "box - sphere": lambda p: np.maximum(box(p), -sphere(p)),
}

# Dense, area-uniform samples of each primitive's surface (fixed seed).
rng = np.random.default_rng(0)
u = rng.normal(size=(400_000, 3))
s_pts = C_S + R_S * u / np.linalg.norm(u, axis=1, keepdims=True)
areas = 4 * np.array([H_B[1] * H_B[2], H_B[0] * H_B[2], H_B[0] * H_B[1]])
n_face = np.round(400_000 * areas / areas.sum()).astype(int)
b_pts = []
for ax in range(3):
    for side in (-1, 1):
        q = rng.uniform(-1, 1, (n_face[ax] // 2, 3)) * H_B
        q[:, ax] = side * H_B[ax]
        b_pts.append(C_B + q)
b_pts = np.concatenate(b_pts)

# Keep the samples that lie on each composite's boundary.
eps = 1e-9
surface = {
    "sphere":       s_pts,
    "box":          b_pts,
    "union":        np.concatenate([s_pts[box(s_pts) >= -eps], b_pts[sphere(b_pts) >= -eps]]),
    "intersection": np.concatenate([s_pts[box(s_pts) <= eps], b_pts[sphere(b_pts) <= eps]]),
    "box - sphere": np.concatenate([s_pts[box(s_pts) <= eps], b_pts[sphere(b_pts) >= -eps]]),
}

# Query grid over [-1, 1]^3.
n = 48
h = 2.0 / (n - 1)
g = np.linspace(-1, 1, n)
P = np.stack(np.meshgrid(g, g, g, indexing="ij"), -1)

print("field          |grad|-1|>0.05  error>0.01  of which |grad|~1  max error in  out  |f|<=|d|")
for name, f in shapes.items():
    F = f(P)
    grad = np.stack(np.gradient(F, h), -1)
    dev = np.abs(np.linalg.norm(grad, axis=-1) - 1.0)
    interior = np.zeros(F.shape, bool); interior[1:-1, 1:-1, 1:-1] = True
    d, _ = cKDTree(surface[name]).query(P.reshape(-1, 3), workers=-1)
    d = np.where(F.ravel() < 0, -d, d)               # sign from the (exact) inside test
    err = np.abs(F.ravel() - d)
    ins, out = F.ravel() < 0, F.ravel() >= 0
    bound = np.all(np.abs(F.ravel()) <= np.abs(d) + 0.01)
    dev, inexact = dev[interior], (err > 0.01)[interior.ravel()]
    hidden = f"{np.mean(dev[inexact] <= 0.05):.1%}" if inexact.any() else "-"
    print(f"{name:13s}  {np.mean(dev > 0.05):13.1%}  {np.mean(inexact):10.1%}  {hidden:>16s}  "
          f"{err[ins].max():11.3f}  {err[out].max():5.3f}  {bound}")

# Sphere tracing a 32 x 24 depth image. World-to-camera X_c = R X_w + t.
Cam = np.array([0.8, -2.2, 1.2])                       # camera centre (world)
z = -Cam / np.linalg.norm(Cam)                         # optical axis looks at the origin
x = np.cross(z, [0.0, 0.0, 1.0]); x /= np.linalg.norm(x)
y = np.cross(z, x)                                     # image y points down
R = np.stack([x, y, z])                                # rows: camera axes; t = -R Cam
W, H, fpx = 32, 24, 30.0
K = np.array([[fpx, 0, (W - 1) / 2], [0, fpx, (H - 1) / 2], [0, 0, 1]])
uu, vv = np.meshgrid(np.arange(W), np.arange(H))
rays = np.stack([uu, vv, np.ones_like(uu)], -1).reshape(-1, 3) @ np.linalg.inv(K).T
D = rays @ R                                           # directions in world: R^T d_c
D /= np.linalg.norm(D, axis=1, keepdims=True)

def slab(c, half):                                     # exact ray-box interval
    with np.errstate(divide="ignore"):
        a, b = (c - half - Cam) / D, (c + half - Cam) / D
    return np.minimum(a, b).max(1), np.maximum(a, b).min(1)

def ball(c, r):                                        # exact ray-sphere interval
    oc = Cam - c
    bq = (D * oc).sum(1); disc = bq**2 - (oc @ oc - r**2)
    sq = np.sqrt(np.maximum(disc, 0))
    return np.where(disc >= 0, -bq - sq, np.inf), np.where(disc >= 0, -bq + sq, -np.inf)

(s0, s1), (b0, b1) = ball(C_S, R_S), slab(C_B, H_B)
s_hit, b_hit = s0 <= s1, b0 <= b1
inf = np.full(len(D), np.inf)
exact = {                                              # first hit by interval CSG along each ray
    "union": np.minimum(np.where(s_hit, s0, inf), np.where(b_hit, b0, inf)),
    "intersection": np.where(s_hit & b_hit & (np.maximum(s0, b0) <= np.minimum(s1, b1)),
                             np.maximum(s0, b0), inf),
    "box - sphere": np.where(~b_hit, inf,
                    np.where(~s_hit | (b0 < s0) | (b0 > s1), b0,
                    np.where(s1 < b1, s1, inf))),
}

def trace(f, eps=1e-4, t_max=6.0, max_steps=500):
    s = np.zeros(len(D)); steps = np.zeros(len(D), int)
    active = np.ones(len(D), bool); hit = np.zeros(len(D), bool)
    for _ in range(max_steps):
        dist = f(Cam + s[active, None] * D[active])
        idx = np.flatnonzero(active)
        steps[idx] += 1
        hit[idx[dist < eps]] = True
        s[idx] += dist
        active[idx[(dist < eps) | (s[idx] > t_max)]] = False
        if not active.any():
            break
    return np.where(hit, s, np.inf), steps, hit

mu = 0.1                                               # TSDF truncation distance
fields = [("union", shapes["union"]), ("intersection", shapes["intersection"]),
          ("box - sphere", shapes["box - sphere"]),
          ("union / 2", lambda p: 0.5 * shapes["union"](p)),
          ("union TSDF", lambda p: np.clip(shapes["union"](p), -mu, mu))]
print("\nfield          hit px  wrong px  |dZ| median     max   steps hit: mean  max   miss: mean")
for name, f in fields:
    s, steps, hit = trace(f)
    ref = exact[name.split(" /")[0].split(" TSDF")[0]]
    zdir = D @ z                                       # depth Z = ray length * cos(angle)
    both = hit & np.isfinite(ref)
    dZ = np.abs(s[both] - ref[both]) * zdir[both]
    print(f"{name:13s}  {hit.sum():6d}  {np.sum(hit != np.isfinite(ref)):8d}  {np.median(dZ):11.1e}  {dZ.max():7.1e}"
          f"  {steps[hit].mean():15.1f}  {steps[hit].max():4d}  {steps[~hit].mean():10.1f}")

# A TSDF on the 48^3 grid: the union's distance clipped to [-mu, mu].
F = shapes["union"](P)
T = np.clip(F, -mu, mu)
band = np.abs(F) < mu
print(f"\nTSDF mu = {mu} ({mu / h:.1f} voxels): {band.mean():.1%} of voxels in the band, "
      f"{np.mean(T == mu):.1%} at +mu, {np.mean(T == -mu):.1%} at -mu")
i = k = n // 2                                         # one grid line along x (y = z = 0.02)
print("x:   ", " ".join(f"{v:5.2f}" for v in g[::4]))
print("SDF: ", " ".join(f"{v:5.2f}" for v in F[::4, i, k]))
print("TSDF:", " ".join(f"{v:5.2f}" for v in T[::4, i, k]))
# Zero crossings along every x-line, by linear interpolation between voxels.
def crossings(V):
    a, b = V[:-1], V[1:]
    c = np.sign(a) != np.sign(b)
    return c, a[c] / (a[c] - b[c])                        # fraction of a voxel step
cF, oF = crossings(F); cT, oT = crossings(T)
Pc = np.stack(np.broadcast_arrays(g[:-1, None, None], g[None, :, None], g[None, None, :]), -1)[cF]
Pc[:, 0] += h * oF
print(f"{cF.sum()} zero crossings; max |SDF| at them: {np.abs(shapes['union'](Pc)).max():.1e}; "
      f"same from the TSDF: {np.array_equal(cF, cT) and np.allclose(oF, oT)}")

Output:

field          |grad|-1|>0.05  error>0.01  of which |grad|~1  max error in  out  |f|<=|d|
sphere                  0.1%        0.0%                 -        0.002  0.004  True
box                     1.4%        0.0%                 -        0.001  0.002  True
union                   1.9%        0.2%             38.3%        0.080  0.003  True
intersection            1.8%        8.4%             91.3%        0.002  0.048  True
box - sphere            2.7%       48.1%             96.8%        0.004  0.352  True

field          hit px  wrong px  |dZ| median     max   steps hit: mean  max   miss: mean
union             158         0      2.4e-05  3.4e-03             11.5   127         8.1
intersection       47         0      2.5e-05  3.4e-04             14.2    33         6.1
box - sphere       65         0      2.4e-05  4.4e-04             13.4    34         6.7
union / 2         158         0      1.4e-04  6.3e-03             26.9   255        16.4
union TSDF        158         0      2.3e-05  3.5e-03             31.9   147        61.6

TSDF mu = 0.1 (2.4 voxels): 9.5% of voxels in the band, 86.5% at +mu, 4.0% at -mu
x:    -1.00 -0.83 -0.66 -0.49 -0.32 -0.15  0.02  0.19  0.36  0.53  0.70  0.87
SDF:   0.40  0.23  0.06 -0.11 -0.28 -0.28 -0.27 -0.43 -0.38 -0.22 -0.05  0.12
TSDF:  0.10  0.10  0.06 -0.10 -0.10 -0.10 -0.10 -0.10 -0.10 -0.10 -0.05  0.10
864 zero crossings; max |SDF| at them: 9.0e-04; same from the TSDF: True

The sphere and box agree with brute force to within the sampling error, confirming the box formula. The composites err only where predicted: the union only inside (up to 0.080), the intersection and difference only outside, the difference on 48% of the grid by up to 0.352, where the nearest box surface has been cut away. All are valid bounds. The eikonal test misses this: it fires on 1.4–2.7% of voxels in the box and the composites, exact or not, at creases and the medial axis, while 38–97% of the inexact voxels pass it.

Sphere tracing classifies every pixel correctly, bounds included. Median depth errors are about 2×10−52 \times 10^{-5}, below the stopping threshold 10−410^{-4}, except for the halved field, whose test allows twice the distance; the maxima are larger because the test f<10−4f < 10^{-4} bounds the distance to the surface, not along the ray, so a nearly tangential ray can stop short of the hit (reasoning from the stopping rule). Halving the field multiplies the mean step count by 2.3, close to Hart’s observation. The TSDF finds its 864 zero crossings at the same positions as the full SDF, within 9×10−49 \times 10^{-4} of the true surface (linear interpolation error), although only 9.5% of voxels carry a distance; tracing it costs more, since no step exceeds μ\mu: 61.6 steps per missing ray instead of 8.1.

Common Misconceptions

  • “∥∇f∥=1\lVert \nabla f \rVert = 1 almost everywhere means ff is a distance.” It is necessary, not sufficient: min and max of exact SDFs satisfy it while underestimating the distance on one side.
  • “A TSDF is an SDF with a smaller range.” Beyond μ\mu it carries only a sign; its gradient vanishes, and it cannot produce normals or closest points there.

Where It Is Used

  • Depth fusion and dense mapping: TSDF grids fuse range images (Curless & Levoy, 1996), multi-view stereo depth maps, and RGB-D frames in dense SLAM.
  • Surface reconstruction: signed fields from oriented point clouds (Hoppe et al., 1992); see 3D reconstruction.
  • Rendering and robotics: sphere tracing of analytic and CSG models (Hart, 1996); clearance and penetration tests.
  • Learned shape models: DeepSDF and later neural fields.

Related

  • Point Cloud

    A set of 3D points sampled from the surfaces of a scene, possibly with color, intensity, or normals per point, and the most direct output of 3D sensors and reconstruction.

  • Depth Map

    An image whose pixels store the distance to the scene, usually the depth Z along the optical axis, so that each pixel with a known camera can be lifted to a 3D point.

  • Mathematical Morphology

    A theory and toolbox for processing images by shape, probing them with a small set called a structuring element through erosion, dilation, and the operators built from them, such as opening, closing, top-hats, and morphological reconstruction.

  • 3D Reconstruction

    Recovering the 3D shape of objects and scenes from images, depth maps, or range scans, as point clouds, meshes, volumes, or implicit and radiance fields, and measuring how accurate and complete the result is.

  • Multi-View Stereo

    Recovering dense 3D geometry, as depth maps, point clouds, meshes, or volumes, from many images whose camera poses and intrinsics are known.

  • Simultaneous Localization and Mapping (SLAM)

    Building a map of an unknown environment while estimating the trajectory of the sensor moving through it, and using recognized revisits to keep both globally consistent.

References

  1. Hart, J. C. (1996). Sphere Tracing: A Geometric Method for the Antialiased Ray Tracing of Implicit Surfaces. The Visual Computer, 12(10), 527–545.
  2. Hoppe, H., DeRose, T., Duchamp, T., McDonald, J. & Stuetzle, W. (1992). Surface Reconstruction from Unorganized Points. Computer Graphics (Proceedings of SIGGRAPH '92), 26(2), 71–78.
  3. Jacobson, A., Kavan, L. & Sorkine-Hornung, O. (2013). Robust Inside-Outside Segmentation using Generalized Winding Numbers. ACM Transactions on Graphics, 32(4), 1–12.
  4. Sethian, J. A. (1996). A Fast Marching Level Set Method for Monotonically Advancing Fronts. Proceedings of the National Academy of Sciences, 93(4), 1591–1595.
  5. Zhao, H. (2005). A Fast Sweeping Method for Eikonal Equations. Mathematics of Computation, 74(250), 603–627.
  6. Curless, B. & Levoy, M. (1996). A Volumetric Method for Building Complex Models from Range Images. Proceedings of the 23rd Annual Conference on Computer Graphics and Interactive Techniques (SIGGRAPH), 303–312.
  7. Park, J. J., Florence, P., Straub, J., Newcombe, R. & Lovegrove, S. (2019). DeepSDF: Learning Continuous Signed Distance Functions for Shape Representation. IEEE/CVF Conference on Computer Vision and Pattern Recognition (CVPR), 165–174.