Core Concepts

Core Concepts

Implicit Representations & Signed Distances in the Metafold API

Unlike discrete, tessellated surface representations—e.g. triangle meshes, the Metafold geometry kernel represents shapes implicitly through shape functions. These implicit shape functions give us the signed distance to the surface (or zero level set) of the shape from a given point position in R3\mathbb{R}^3. By querying the surface distance at a sufficient number of varied point positions (often referred to as “sampling the shape”), we are able to build an approximate picture of the implicit shape. The detail of this “picture” depends on the number of points we use to sample the shape, and the strategy we use to vary the point positions. The higher the shape’s complexity, the more points are needed to capture the surface in sufficient detail, and vice versa.

💡
This is why implicit shapes often fail to capture features with sharp edges. With an infinite number of evenly distributed sample points we would have a complete representation of the shape, but of course each sample requires a certain amount of memory, and we don’t have an infinite amount of memory. We will revisit the inverse relationship between precision and memory multiple times in this document. The bottom line is: sample density will vary depending on your use case. For example, sampling an implicit shape for stereolithography (SLA) or digital light processing (DLP) 3D printing will demand enough points to meet the specific manufacturing tolerances of the printer involved, i.e. number of slices x number of pixels per-slice supported by the UV projector; whereas generating a triangle mesh from the implicit shape can likely get away with much fewer points.
Sampling an implicit shape (in purple) at two different resolutions. Denser sampling (left) is able to better capture the corners of the implicit shape vs. the sparse grid (right).
Sampling an implicit shape (in purple) at two different resolutions. Denser sampling (left) is able to better capture the corners of the implicit shape vs. the sparse grid (right).

Sampling implicit shapes using a 3D grid of uniformly distributed points is a naive, yet effective strategy to capture detail without knowing more about the shape upfront. As implicit shapes have no bounding box per se, choosing an appropriate position and size for the uniform grid can be challenging. If the shape is as simple as an ellipsoid, then grid placement is trivial provided you know the ellipsoid center and size. If the shape is a lattice however, our implementation repeats the lattice unit cell infinitely in all directions in R3\mathbb{R}^3, so there is no obvious grid placement!

It’s a little like shining a torch into a dark room full of objects, without first scanning the room once with the light, it’s hard to know where to focus the torch light to get more detail on the object you care about.

Sampling a composition of shapes. Only partial regions of each shape are sampled in the chosen window.
Sampling a composition of shapes. Only partial regions of each shape are sampled in the chosen window.

Things get more complex when the shape function is composed of multiple shapes, and even more tricky when coordinate transforms are involved. We therefore leave grid placement up to the designer of the shape function, who is the most informed to make this decision.

💡
The tradeoff with using a uniform 3D grid of points is the memory required to hold the inputs, outputs, and any intermediate values involved in the computation. At worst, the space requirement increases in a cubic manner:
image

We could store results using a sparse data structure, but this turns out to have little-to-no (or worse) space savings for highly complex microstructures, e.g. a design space filled with a dense lattice, which are a key benefit of using implicit shapes. The current binary output format is also incredibly fast and simple to read and write, allowing our customers to get going quickly without any special decoders needed. We could do better, but we could also do much worse in terms of storage. Tip: You can use the export_vdb job to output sparse level set data in OpenVDB format if desired.

With signed distances sampled onto a grid (referred to as a volume), we can render the shape by further sampling the grid values. Depending on how far apart the grid points are, some interpolation of grid values may be required to approximate distances between the available points. We can also use this method to sample volumes in other shape functions (TODO: Link). Interpolating grid values is computationally cheap and fast, this is particularly convenient for us as it allows us to cache the results of comparatively slow and expensive shape functions, e.g. sampling a triangle mesh (TODO: Link), and use the cached volume in subsequent shape functions—trading precision for processing speed.

💡
As always it is important to choose an appropriate resolution to cache the expensive shape function at. Higher resolutions will capture more detail, but the same story applies here: the resulting volume will require more memory to store. In practice you can get away with relatively low resolution grids, since the interpolation in the subsequent sampling does a pretty good job of approximating the cached shape.
Left: Sampling a shape onto a low resolution grid to produce a cached volume. Right: Sampling the cached volume onto a comparatively dense grid composed with another shape.
Left: Sampling a shape onto a low resolution grid to produce a cached volume. Right: Sampling the cached volume onto a comparatively dense grid composed with another shape.

Further Reading