Nonetheless, it is more than fast enough to keep up with
currently available printers.
The architecture of our implementation is shown in
5. 1. Setup
Figure 3 and proceeds as follows:
Precompute acceleration structures
for each slab, in printer order:
for each shape overlapping slab:
Compute surface microgeometry and attributes
Compute voxels and material composition
Normalize and dither materials to device capability
Output slab to printer
We begin by calculating bounds for each shape in the
scene. Because fablets can displace surface geometry, this
is not known directly from the input. Users provide maximum displacement bounds, but we additionally execute
an interval arithmetic variant of the surface fablet stage to
automatically infer displacement bounds as well6 and use
the minimum of the user-provided and inferred bound.
We next create acceleration structures to speed up the
nearest surface point queries performed in the volume fablet stage. We build a bounding volume hierarchy (BVH) that
spatially partitions the base primitives of the input mesh,
conservatively accounting for possible displacement using
the displacement bounds calculated in the prior stage. This
upfront process is fast because it is performed on the untessellated base primitives of the input.
If the target printer requires support structures, we precalculate the places where such support is needed. We use
a fast, high-resolution, fixed-point rasterizer to perform an
orthographic Z-buffer rendering along the print platform
movement (z) axis. We conservatively dilate each primitive
to account for any possible displacement using the bounds
calculated in the first stage. The resulting depth map contains the highest point along the z axis at which the material
is present for each voxel column represented by the given
depth sample. During the later output phase, if a given voxel
is void we output support if and only if the height of that voxel
is lower than the highest populated voxel for that particular
voxel column as recorded in the depth map (Figure 4).
5. 2. Slab processing (outer loop)
To begin printing, we subdivide the print volume into slabs.
The size of the slab is dynamically calculated based on target memory usage and is a function of the resolution of the
print and the total build volume. As we process each slab,
we maintain a working set of shapes whose bounding volume intersects the current slab. As we begin the processing
of each slab, we update the working set by removing shapes
that are now beyond the current slab and adding ones that
are now under the slab’s domain.
5. 3. Shape processing (inner loop)
Within the working set, we sort objects by user-provided
priority, processing from the highest to lowest priority and
immediately discarding any newly generated voxels that
are already occupied. Early culling makes fablet evaluation
efficient: only one fablet (the one assigned to the highest pri-
ority object) gets evaluated per voxel.
The first stage of the per-shape loop performs partial tes-
sellation. Primitives can also be tessellated on demand in
order to perform the distance function or the nearest sur-
face attribute queries. We always tessellate into micropoly-
gons, our common 2D primitive for the remainder of the
Figure 3. The architecture of the OpenFab implementation is
designed to stream over large, high-resolution print volumes
with a fixed memory budget. The printing volume is divided into
slabs along the primary printer axis, sized to bound memory usage.
The pipeline processes one slab at a time and streams the output
to the printer. Minimizing the amount of precomputation before
streaming begins keeps startup time to a minimum, letting the
printer start working almost immediately after OpenFab begins
processing. Intermediate results such as tessellated geometry that
span slab boundaries are cached for reuse, and the caches are also
set to a fixed maximum size.
z sort objects
build nearest query
priority sort objects
find objects in slab
surface fablet stage
volume fablet stage