Last updated on

How to Compute a Signed Distance Field from a Mesh

ENNLESPT-BR


A sampled signed distance field is a precomputed grid of distance samples that stands in for a shape with no compact distance formula. Triangle meshes are the most common source: characters, scanned models, level geometry, and font outlines all arrive as lists of vertices and faces rather than equations. Sampling turns that mesh into a field you can query anywhere in space.

This article builds on the analytical signed distance fields tutorial and assumes the sign convention is familiar: negative inside, zero on the surface, positive outside. Here the question changes from “what is the formula” to “how do I compute and store the distances at all”.

Why a Mesh Has No Closed-Form SDF

An analytical SDF works because the shape has structure the formula can exploit. A sphere is defined by a center and a radius, so its surface distance is the distance to the center minus the radius. A box is defined by half-extents, so its distance comes from a few clamped components. The formula holds everywhere in space and costs a handful of arithmetic operations to evaluate.

A triangle mesh offers no such shortcut. It is an arbitrary collection of triangles with no shared center, axis, or parameterization, so no expression returns the distance to the nearest surface. You can write a closed-form distance from a point to a single triangle: project the point onto the triangle’s plane and clamp to the triangle, and the length of that difference is the distance. The difficulty is the nearest part. The true distance is the minimum over every triangle, and finding that minimum requires a search. The inside and outside test adds a second global question that no per-triangle formula can answer on its own.

That is the gap sampling fills. It evaluates the expensive search at a finite set of grid points, stores the answers, and lets interpolation answer the rest.

The Mesh-to-SDF Sampling Pipeline

Turning a mesh into a signed distance field is often called voxelizing the mesh, because the output is a volume of cells (voxels) that each hold a distance value. The pipeline has four steps.

1. Place a Grid over the Mesh

Start with the mesh’s bounding box and pad it slightly so the field has room to go positive outside the surface. Then choose a resolution, the number of samples along each axis. The grid becomes the representation: once sampled, queries go to this grid and never touch the triangles again. Resolution is the main accuracy knob and the main memory cost, and the next section makes that tradeoff concrete.

2. Find the Distance to the Nearest Triangle

For each grid sample point p\mathbf{p}, compute the distance to every candidate triangle and keep the smallest. The distance to one triangle tt is the distance to the closest point on it:

dt(p)=∥p−ct(p)∥d_t(\mathbf{p}) = |\mathbf{p} - \mathbf{c}_t(\mathbf{p})|

where ct(p)\mathbf{c}_t(\mathbf{p}) is the closest point on triangle tt. That closest point lands either inside the triangle, on one of its edges, or at a vertex, and the same projection and clamping handles all three cases. The unsigned mesh distance is the minimum over the triangles:

dsurface(p)=min⁡tdt(p)d_{\text{surface}}(\mathbf{p}) = \min_t d_t(\mathbf{p})

Testing every triangle for every sample is correct but slow, so production pipelines accelerate the search with a bounding volume hierarchy or a spatial grid that rejects distant triangles early. Another approach writes the raw distance only near the surface and then propagates it through the grid with a distance transform sweep, which reaches the same result in a different order.

3. Decide Which Side Each Sample Is On

Distance alone gives the magnitude, and the sign needs an inside and outside test. For a watertight mesh, the standard method casts a ray from the sample and counts how many triangles it crosses: an odd count means inside, an even count means outside. A winding number test is more robust when triangles overlap or the mesh has small defects, and the generalized winding number extends it to meshes that are not perfectly closed. A cheaper option for watertight meshes floods the outside region from the grid boundary and marks everything the fill cannot reach as inside.

This step is where mesh SDFs most often go wrong. If the mesh has holes, duplicate faces, or inconsistent winding, the sign flips near the defect and the sampled field develops inside-out patches. Good implementations validate watertightness before sampling, or fall back to a winding number test that tolerates the imperfection.

4. Store the Samples and Interpolate

Store the signed samples in a 3D texture or a flat array. At runtime, a query reads the eight samples surrounding it and blends them with trilinear interpolation. The result is an approximate distance that stays continuous across cell boundaries and is cheap enough for a shader.

Inside Outside Surface

The visualization shows the same four steps in 2D, where the mesh is a polygon and the nearest feature is an edge instead of a triangle. Press play to step through the grid in scanline order: each sample finds its closest edge and records a signed distance, marked red inside, blue outside, and white on the boundary. Turn on the reconstructed field to see bilinear interpolation fill the gaps between samples, and compare the dashed polygon outline against the colored zero-contour. At low resolution the contour rounds off corners and clips thin features; raising the resolution pulls the reconstructed boundary back toward the true edges.

Resolution, Memory, and Missing Detail

The grid size grows quickly with resolution. Doubling the number of samples per axis multiplies the sample count by 4 in 2D and by 8 in 3D. A 128³ grid holds about 2 million samples, while a 256³ grid holds over 16 million. At two bytes per sample, a 256³ field is roughly 32 MB, and at four bytes it is 64 MB, before any compression.

That cost buys accuracy, and corners are the hardest place to spend it. The true distance changes direction abruptly across a corner’s bisector, while trilinear interpolation can only produce a smooth blend of the surrounding samples, so the reconstructed zero-contour rounds the corner. The same effect makes features thinner than a voxel disappear from the field entirely. Raising the resolution shrinks both errors, but the improvement is gradual and never exact.

Two standard tricks reduce the cost rather than the error. A narrow band stores samples only within a few cells of the surface and skips the uniform interior and exterior, where the distance is predictable. Sparse storage and run-length encoding then compress the large empty regions. Both keep query behavior the same while cutting the memory a full grid would need.

Working with a Sampled Field

Once a field is sampled, it feeds the same downstream pipeline as an analytical SDF. CSG operations consume scalar values, so union, intersection, and subtraction work unchanged on sampled fields. Surface normals still come from finite differences across neighboring samples, and ray marching still steps through the field.

The important practical difference is step safety. An analytical SDF never overestimates the distance to the surface, which is what makes sphere tracing safe with steps equal to the field value. Trilinear interpolation does not preserve that guarantee: the interpolated value can exceed the true distance between samples, especially near corners. Ray marchers treat a sampled field as a conservative lower bound, widen their safety margins, and use a smaller step multiplier.

Mesh Distance Fields in Production

Unreal Engine 5 samples static triangle meshes into sparse voxel grids and uses the resulting mesh distance fields for distance field shadows, ambient occlusion, and collision queries. The precomputation runs offline during content authoring, so runtime cost is grid lookups and interpolation. Physics engines rely on the same representation for collision between complex meshes, often at a coarser resolution that trades spatial accuracy for speed and can be rebuilt each frame for deformable objects.

The 2D ancestor of this pipeline is Valve’s signed distance field text rendering. Glyph outlines are sampled into a distance texture once, and hardware bilinear filtering reconstructs crisp, anti-aliased edges at any magnification. The mesh-to-SDF pipeline in this article is the 3D version of that same idea.

When the mesh changes every frame, sampling a grid may be too slow. A runtime alternative evaluates the nearest-triangle distance directly from a bounding volume hierarchy at each query, trading precomputation for a fresh search. Precomputed grids remain the better fit when the geometry is static and queries are frequent.

Beyond Uniform Grids

Two extensions address the limitations of a uniform grid.

Adaptively sampled distance fields (ADFs) replace the grid with an octree that samples densely near surface features and sparsely in flat regions, cutting memory while preserving detail. The tradeoff is traversal: queries navigate a tree instead of indexing a flat array, which is harder to accelerate on fixed-function GPU hardware.

Multi-channel signed distance fields (MSDFs) store distances to several nearby edges per cell, using extra channels in a texture. Interpolation takes the minimum across channels, which preserves the sharp corners that a single-channel field rounds off, at three to four times the memory. MSDFs are standard in high-quality text rendering and are common for vector graphics on GPUs.

Both build on the same sampling principle: sample the true distance, store it, and reconstruct it at runtime.