Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Show HN: 3D Meshes of Signed Distance Functions in Python (github.com/fogleman)
157 points by fogleman on Feb 16, 2021 | hide | past | favorite | 51 comments


Hi HN.

Well, I wanted to make a 3D model and 3D print it, but I just didn't want to use OpenSCAD. So I ended up making a whole new 3D modeling library based on signed distance functions. I'm pretty happy with how it turned out so far, so I thought I would write a nice README and share it here.

It's certainly complete enough that you could try it out and make your own 3D models - let me know if you make anything interesting!


That's pretty cool. I see you are using marching cubes. How are you handling sharp geometry (I'm assuming previews use a finite difference for normals)?

Also I see you are at formlabs. I'm sure you know of Matt Keeter's libfive :)


Yes, I'm well aware of libfive!

I'm not doing anything special for sharp geometry. For now you just need to use sufficient resolution until it looks "good enough." Normals are just based on the triangle normals.


Take the spatial derivative for the normals? Runga-kutta methods work wonders for that: https://en.m.wikipedia.org/wiki/Runge–Kutta_methods That works well if they are true signed distance.


Since you have a distance, you could fairly easily implement dual contouring, I think?


Or Surface Nets. Nice compromise of speed and quality. Marching cubes needs a lot of resolution and it's imperfections are especially ugly my eyes.

Maybe mesh resolution isn't a problem for 3D printing. But if 3D printing is the main goal why bother with a mesh? Just go straight to voxels that can be fed directly to the printer.


The printer does not use voxels, it uses G-codes, which are tool paths for the print head.


True for FDM, but for SLA you have the potential rendering a direct slice of the SDF.


OK. But a voxel representation has to be a closer match than a mesh. I guess overhang, solidity etc still need to be calculated and dealt with. Which is non-trivial.

Is it that "mesh to G-code" libraries are more mature than "voxel to G-code"?


This is great. Thank you for making it! The api layout tells me you are a nice person.


Love the library! I did not know about signed distance functions, and I am just amazed of the complex shapes that can be made from simple primitives.

Thank you!


If you want to go even deeper than that: https://www.youtube.com/watch?v=8--5LwHRhjk


What a beautiful library and api! I don't 3D print, but I'll be studying your code as a master-class in how python should be done.


out of curiosity, why did you not want to use OpenSCAD?


OpenSCAD is pretty rigorous regarding defining relative geometry. Like shifting origin and sketch on plane rotated 30° around X. It leads to unnecessary math to place objects.


It can be a bit cumbersome sometimes, but the only thing that really slows me down is the rendering performance. As soon as the detail level exceeds a certain range it basically becomes a bottleneck the size of a singularity.

Just the last few days I spent hours creating an external preprocessor for a project so I can feed as little data as necessary into OpenSCAD. Luckily it kind of worked and got me from "process gets killed after 70 minutes" down to "finished rendering in 5 minutes".

Still it's my favorite CAD software because the coding paradigm just feels so much more intuitive and simple than what FreeCAD and others do.

I'm definitely going to try out this project, though; it looks very promising as well.


(to the channel), I am sure you know about it, but this [1] technique for making a big design a tree of smaller designs works ok.

I feel like OpenSCAD is like 2 standard deviations away from perfection. Functions vs Modules and how clunky composition and application of operators over the object tree. It almost needs to be Datalog. Right now, it is a notch under SQL.

[1] https://old.reddit.com/r/openscad/comments/lbq58y/my_approac...


The problem in my case was that I wanted to combine a pattern with my objects that I precomputed using an external program because it contained a lot of curved surfaces. As I already knew OpenSCAD basically becomes unusable if you try to use a png image as heightmap I decided to instead generate a .scad file containing an array with precomputed coordinates, and then used that to build and extrude 2d polygons.

This kind of works for the 3d preview, but the stl renderer was not happy regardless. It was basically the worst case for OpenSCAD. But still, the fact I could just auto-generate a text file and use it directly for building models is fantastic. With tools like FreeCAD I wouldn't even know where to start, here it was basically a case of "it just works" and with a few more minutes it can be combined with a bash script for batch rendering.


Isn't this par for the course for constructive methods though? I agree that some things that are easy in a classic constraint-based CAD package are a huge burden in OpenSCAD. However, I don't see how signed distance fields would change that.


So elegant. Nice job!


Not sure how this will be received in HN, but I just want to say... when I grow up, I want to be like fogleman :-). Just look at his portfolio of truly great projects [1].

Something that I find inspiring in his work is that he seems to use pretty !boring! tech (imho). No rust?! No lisp/reactive/functional/esoteric programming!? what?! You can't be cool if you use mainstream tech like go or python, right? Well, it turns out, you can!

Well done sir :-)

1: https://www.michaelfogleman.com/


I absolutely prefer "boring" tech.

Thanks for the kind words.


I bought a pen plotter a few years ago inspired in part by your work - it's been a ton of fun (and paid for itself multiple times over now :)


I’m curious to know, what do you use a pen plotter for?


Plotting neural data and generative art mostly.


It would be really cool to have a library like this that could spit out a shader for real-time rendering with ray marching on your GPU and then use marching cubes to compute the final STL when you’re done prototyping.


I am also a big fan of iquilez, and a little fed up with openscad. I recently tried out deadsy/sdfx, a similar tool written in golang. I was impressed and I enjoyed using it, but there was a little too much friction when I had a complicated design that I just wanted to make and print ASAP.

I'm a little more comfortable in python, plus I think the language is better suited to this, as a DSL or something like it. I'll definitely try it out, thank you for sharing.


Let me know if you do anything cool with it!

I just got text working, which is very exciting. It was surprisingly easy, too.

1) Render text to a high resolution 1-bit array using PIL 2) Compute the distance transform using scipy 3) Do some index fiddling to map it into world space, and it's done!

Even works with unicode: https://twitter.com/FogleBird/status/1361523405821005824


Really neat! I can use this for CFD simulations where geometries need to be optimized; OpenFOAM should snap on to these stls just fine! https://www.openfoam.com/documentation/guides/latest/doc/gui...


Nice! Do you have any plans to publish to PyPI? Is there an API to access numpy array containing the evaluated SDF volume?


I suppose I should put it on PyPI! There isn't such an API yet but it wouldn't be too difficult to add.


Impressive work!

CadQuery is another library for programming 3d-models in Python. A good alternative to OpenSCAD. Has integration in FreeCAD but can also be used stand-alone.


Most Engineering software uses surface models or meshes today. Are there specific advantages of using SDFs here instead?


Generative design is almost always in SDF form. Things like point clouds, images, and 3DNN also dovetail nicely. SIMP in topology optimization is a good example also. I believe alot of SDF applications are still held back by mesh extraction. There is no silver bullet that can handle adaptive methods, sharp features, and generate a manifold.

SDF and mesh extraction are one of my favorite areas of research. I think it is very important for additive manufacturing in particular. The value will be hybrid SDF and spline methods for complex and highly integrated applications such as fluid and heat transfer or compliant mechanisms.

Modeling a box or cylinder with SDF isn't the right application IMO. Optimized topology for a given PDE is.


> Modeling a box or cylinder with SDF isn't the right application IMO. Optimized topology for a given PDE is.

Like anything in engineering and design, IMO, it depends on who you are what you are doing.

> SDF and mesh extraction are one of my favorite areas of research.

Why convert a SDF to a mesh to do visualization and engineering analysis? Even with FDM, which can be thought of a 2D filling problem like scan conversion once you have a SDF, meshless may be the way forward with additive manufacturing as well. One can think of reasons you would have to do mesh extraction, but for niche areas of manufacturing.


Especially with additive manufacturing, SDF's are far superior but still not widely used (but I predict this will change very soon). As others have said, many operations are much easier when working with SDF's, but specifically for AM, there are many operations like lightweighting that are extremely difficult to do "correctly" when using normal meshes. For simulations as well, an SDF makes things like voxelization, random walks, and basically everything else that is related to the topology of a part much easier.

Check out the company/product nTopology. It's IMO the future for engineering modelling and CAD for AM.

I used to work for an AM company and was essentially the internal evangelist that complained about STL's and "traditional" CAD formats that are proprietary or just outright a PITA to work with (I'm looking at you, STEP...)


Super niche, but SDF shaders are the approach used with the voxel art tool, MagicaVoxel, to help artists quickly generate geometry/shapes for their artwork.


I believe it is because sdf are very easy to do constructive solid geometry on. Intersect, union, subtract operations are basically free and robust. Where as with solid geometry robust csg booleans are highly problematic with rounding errors and edge cases galore.


The simplicity is also why the demoscene has made extensive use of them (see the https://iquilezles.org/ site for some great examples) --- very complex scenes can be described in a tiny amount of space.


Looks pretty cool! Kind of reminds me of Google sketchup but in code.


Is it ok if I make a Blender node system out of this?


Sure.


Is it possible to plug these into a VR/AR system?


Can they load an STL?


I don't think rendering is the end goal here - the bigger potential is real-time manipulation of SDFs similar to Dreams geometry editor.

As for rendering, the SDF can be rasterized in shader, or it can be cube-marched to mesh and rendered in standard pipeline. It would be great to hear your opinions on both methods in context of real-time graphics.

Edit: Here's a video of Dreams shape manipulation https://youtu.be/D4Kk36oqGMY?t=589


By rasterizing the SDF in the shader, are you referring to slicing multiple planes through the volume and sampling the function in the shader (as described in https://developer.nvidia.com/gpugems/gpugems/part-vi-beyond-...), or some other technique I'm not aware of?


Well-formed SDFs are extremely efficient to raycast.

The basic idea is that if you evaluate the SDF at a given point, you get the distance to the nearest surface. Thus, you know it is safe to step that far in any direction without crossing a surface. So you can step along a ray with a step size determined by the SDF at each point to quickly converge on a surface.


That is a good explanation of ray casting with ray marching as optimization. Here is interactive implementation from Quilez: https://www.shadertoy.com/view/Xds3zN

How would you end up with badly-formed SDFs?


Honestly, most SDFs you use in practice will be badly formed. Creating a well-formed SDF is very, very hard, and only possible in a few special cases.

So in practice what you try to do is find an SDF that is not TOO far off from what it should be. Ideally, it should always have an absolute value that is equal to or less than (but not by too much) the real, ideal SDF.


Depending on your goals - the failure modes of non-well-formed SDFs are often quite interesting. ;-)


Very cool, love the simple syntax




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: