SDF Utilities¶
isoext.sdf is a small toolbox of signed distance functions for
testing and demos. Nothing in the library requires it: extraction
only sees the values you put on the grid, however you compute them.
Working with Grids shows examples with raw PyTorch.
import torch
import isoext
from isoext.sdf import *
from isoext import viewer
grid = isoext.UniformGrid([128, 128, 128])
Meshes¶
TriangleMeshSDF turns a triangle mesh into a signed distance function, so a
mesh can be sampled into a grid, edited or combined like any other
field, and extracted again.
TriangleMeshSDF(vertices, faces, signed=True)
isoext.assets.load_mesh downloads a few well-known test meshes on
first use and caches them:
"armadillo"(Krishnamurthy and Levoy [1996]),"bunny"(Turk and Levoy [1994]) and"dragon"(Curless and Levoy [1996]), courtesy of the Stanford Computer Graphics Laboratory, for research use"spot"from Crane [2012], public domain
The armadillo, sampled on a 256 cell grid and extracted again:
vertices, faces = isoext.assets.load_mesh("armadillo")
armadillo = TriangleMeshSDF(vertices, faces)
fine = isoext.UniformGrid([256, 256, 256])
fine.set_values(armadillo(fine.get_points()))
v, f = isoext.dual_marching_cubes(fine)
print(f"{len(faces):,} mesh triangles in, {len(f):,} out")
viewer.embed(v, f, color="lightsteelblue")
345,944 mesh triangles in, 204,664 out
The distance is the distance to the closest triangle, found through a
bounding volume hierarchy on the GPU (Ericson [2005];
Jones et al. [2006] survey the approaches). The sign is the generalized
winding number (Jacobson et al. [2013]), evaluated with the tree-based
approximation of Barill et al. [2018], so meshes with holes like the
bunny and the dragon still get a sensible sign. sign="parity" counts
ray crossings instead (Nooruddin and Turk [2003]), which needs a closed
mesh.
The gradient is the direction away from the closest point, so
get_sdf_normal and project_to_surface work on a mesh SDF, and
closest_points returns the closest point on the mesh and its triangle
directly:
p = torch.tensor([[0.0, 0.0, 0.9], [0.5, 0.5, 0.5]], device="cuda")
q, face = armadillo.closest_points(p)
print("distances:", [round(d, 4) for d in armadillo(p).tolist()])
print("closest points:", [[round(x, 4) for x in row] for row in q.tolist()])
print("faces:", face.tolist())
distances: [0.1756, 0.1375]
closest points: [[-0.01, -0.0093, 0.725], [0.6087, 0.4526, 0.5696]]
faces: [281187, 12731]
Primitives¶
SphereSDF¶
SphereSDF(radius: float)
grid = isoext.UniformGrid([128, 128, 128])
sphere = SphereSDF(radius=0.7)
grid.set_values(sphere(grid.get_points()))
v, f = isoext.marching_cubes(grid)
viewer.embed(v, f)
TorusSDF¶
TorusSDF(R: float, r: float) # R=major radius, r=tube radius
torus = TorusSDF(R=0.6, r=0.2)
grid.set_values(torus(grid.get_points()))
v, f = isoext.marching_cubes(grid)
viewer.embed(v, f, color="gold")
CuboidSDF¶
CuboidSDF(size: list[float]) # Full size in [x, y, z]
cube = CuboidSDF(size=[1.0, 1.0, 1.0])
grid.set_values(cube(grid.get_points()))
v, f = isoext.marching_cubes(grid)
viewer.embed(v, f, color="salmon")
MandelbulbSDF¶
A distance estimator for the Mandelbulb fractal of White and Nylander [2009]. The values approximate the distance to the surface rather than being an exact SDF; fewer iterations give a smoother shape.
bulb = MandelbulbSDF(iterations=6)
# The bulb needs slightly larger bounds than the shared grid above
bulb_grid = isoext.UniformGrid([128, 128, 128], aabb_min=[-1.2, -1.2, -1.2], aabb_max=[1.2, 1.2, 1.2])
bulb_grid.set_values(bulb(bulb_grid.get_points()))
v, f = isoext.marching_cubes(bulb_grid)
viewer.embed(v, f, color="coral")
CSG Operations¶
Combine shapes using Constructive Solid Geometry:
Operation |
Description |
|---|---|
|
Combine shapes (min of SDFs) |
|
Keep overlap (max of SDFs) |
|
Invert inside/outside |
|
Smooth blend with radius k |
# Sphere with a hole drilled through it
sphere = SphereSDF(radius=0.7)
hole = CuboidSDF(size=[0.3, 0.3, 2.0])
drilled = IntersectionOp([sphere, NegationOp(hole)])
grid.set_values(drilled(grid.get_points()))
v, f = isoext.marching_cubes(grid)
viewer.embed(v, f, color="orchid")
Transformations¶
Transform |
Description |
|---|---|
|
Move by |
|
Rotate around axis (degrees by default) |
# Two spheres with smooth blending
s1 = TranslationOp(SphereSDF(radius=0.4), offset=[-0.3, 0, 0])
s2 = TranslationOp(SphereSDF(radius=0.4), offset=[0.3, 0, 0])
blended = SmoothUnionOp([s1, s2], k=0.15)
grid.set_values(blended(grid.get_points()))
v, f = isoext.marching_cubes(grid)
viewer.embed(v, f, color="tomato")
References¶
Venkat Krishnamurthy and Marc Levoy. Fitting smooth surfaces to dense polygon meshes. In Proceedings of the 23rd Annual Conference on Computer Graphics and Interactive Techniques (SIGGRAPH '96), 313–324. 1996. doi:10.1145/237170.237270.
Greg Turk and Marc Levoy. Zippered polygon meshes from range images. In Proceedings of the 21st Annual Conference on Computer Graphics and Interactive Techniques (SIGGRAPH '94), 311–318. 1994. doi:10.1145/192161.192241.
Brian Curless and Marc Levoy. A volumetric method for building complex models from range images. In Proceedings of the 23rd Annual Conference on Computer Graphics and Interactive Techniques (SIGGRAPH '96), 303–312. 1996. doi:10.1145/237170.237269.
Keenan Crane. Keenan's 3D model repository. 2012. URL: https://www.cs.cmu.edu/~kmcrane/Projects/ModelRepository/.
Christer Ericson. Real-Time Collision Detection. Morgan Kaufmann, 2005. ISBN 9781558607323.
Mark W. Jones, J. Andreas Bærentzen, and Milos Sramek. 3D distance fields: a survey of techniques and applications. IEEE Transactions on Visualization and Computer Graphics, 12(4):581–599, 2006. doi:10.1109/TVCG.2006.56.
Alec Jacobson, Ladislav Kavan, and Olga Sorkine-Hornung. Robust inside-outside segmentation using generalized winding numbers. ACM Transactions on Graphics, 32(4):33:1–33:12, 2013. doi:10.1145/2461912.2461916.
Gavin Barill, Neil G. Dickson, Ryan Schmidt, David I. W. Levin, and Alec Jacobson. Fast winding numbers for soups and clouds. ACM Transactions on Graphics, 37(4):43:1–43:12, 2018. doi:10.1145/3197517.3201337.
Fakir S. Nooruddin and Greg Turk. Simplification and repair of polygonal models using volumetric techniques. IEEE Transactions on Visualization and Computer Graphics, 9(2):191–205, 2003. doi:10.1109/TVCG.2003.1196006.
Daniel White and Paul Nylander. The mandelbulb: the unravelling of the real 3D mandelbrot fractal. 2009. URL: https://www.skytopia.com/project/fractal/mandelbulb.html.