Isosurfaces / Signed Distance Fields


Here’s a script that lets you input an implicit equation as a string and makes a level set/isosurface mesh.
Isosurfacer.gh (17.8 KB)

This is a version of the method given by Paul Bourke here.
(in particular this tetrahedral version), which @laurent_delrieu had already kindly shared a translation of into Rhino/C# here.
This takes Laurent’s script as a starting point and makes a few improvements and speed ups.

The file contains 2 versions - one which lets you input the equation as text, and one where the equation is given in a method in the code.
The text input one uses Grasshopper’s expression parser, and is slower.
To modify the code one, you don’t need to know much C# though, just open the editor, scroll down to line 158 where it says public double Evaluate and replace the function.

8 Likes

Very nice Daniel, thanks for the improvements, they are very welcome.
Here a script identical to IsoSurface from Millipede. Millipede still very fast (10x) but output Invalid Mesh. So must be healed by
mesh.Vertices.CombineIdentical(true, true);
mesh.Faces.CullDegenerateFaces();


Isosurface Daniel Piker Delrieu.gh (14.4 KB)

With Millipede all the work happens upstream in the evaluate component because it forces you to give it the value of the field at every grid point as an input. This is exactly what I wanted to avoid.
Here for the same function and resolution Millipede takes over 150x as long as the script posted above.


Isosurfacer_Millipede_comparison.gh (17.7 KB)

Look at my previous post I made a similar script as Millipede, suppressing the calculation and using a noise component to generate the values. Does I miss something ?

With both the examples in your file I think a bottleneck is generating and passing the huge list of all grid points and values before the isosurfacing component. It would be interesting to compare this with putting the noise generation method inside the Evaluate function, like the example in the first post of this thread.
(also when the field evaluation only happens once upstream, it means the component cannot make use of it for calculating the normals)

Noise is also relatively cheap to generate - for most of the things I’d like to use this for, such as functions using a lot of calls to trig functions like for triply periodic minimal surfaces, evaluating the function at the points is where the majority of the time is spent, and having to generate all these values upstream is much much slower - over 10 seconds for the gyroid example above with Millipede, compared to only 66 milliseconds with this script.

I think what would be even nicer would be to make the Evaluate method part of an interface, so you could have upstream components which define a field (without having to actually provide millions of samples of it) then pass them to the solver which then call this Evaluate method only as needed.
You could also have methods for adding, subtracting and blending them (like this and this).
This could also make it possible to use hierarchical methods in the contouring, where you subdivide cells to a finer resolution only when you know the surface passes through them, to avoid evaluating the field in so many places where it isn’t needed.

Dendro does a lot of this and is super fast, but what I still miss there is the possibility to define completely custom fields or mathematical functions.

1 Like

Added a few more of the primitives and blending functions from here
https://www.iquilezles.org/www/articles/distfunctions/distfunctions.htm

It’s a lot of fun to play with.

SDF_unions3.gh (13.6 KB)

3 Likes