I have to admit that when generating isosurfaces, I’ve come to prefer polynomial-trigonometric functions over noise arrays. Where noise gives samey-looking blobs in all directions, PTFs (polytrigs?) provide structure and asymmetry at low cost.
Here’s the source function I used to generate those surfaces.
var k1 = M.rng.get(-1, 1); var k2 = M.rng.get(-1, 1); var k3 = M.rng.get(-1, 1); var source = function(x, y, z) { if (y < 0.1) return -1; var a = x + y + k1; var b = y + z + k2; var c = z + x + k3; var d = a + b * c; var e = a * b + c; return Math.cos(a * d + b * d + c); };
Rolling up a few random offsets gives us a variety of surfaces. The specifics of the math don’t matter: I just want a polynomial that’s complex enough to be interesting. We take the cosine to smooth the results and limit them to the range (-1, 1).
For the field function, I’ve dropped the cube-based point field thingy I used in the last couple of posts in favor of a modified 3D interpolator.
var field = function(x, y, z) { function lerp(y0, y1, mu) { return mu * y1 + (1 - mu) * y0; } var xi0 = Math.floor(x); var mux = x - xi0; var yi0 = Math.floor(y); var muy = y - yi0; var zi0 = Math.floor(z); var muz = z - zi0; var xi1 = xi0 + 1; var yi1 = yi0 + 1; var zi1 = zi0 + 1; var i1, i2, i3, i4; i1 = lerp(source(xi0, yi0, zi0), source(xi0, yi0, zi1), muz); i2 = lerp(source(xi0, yi1, zi0), source(xi0, yi1, zi1), muz); i3 = lerp(i1, i2, muy); i1 = lerp(source(xi1, yi0, zi0), source(xi1, yi0, zi1), muz); i2 = lerp(source(xi1, yi1, zi0), source(xi1, yi1, zi1), muz); i4 = lerp(i1, i2, muy); return lerp(i3, i4, mux); };
It’s a stripped-down version of the code I use to generate isosurfaces from noise arrays. It uses linear rather than cosine interpolation. Initially, I made the change to improve its performance, but found that it also allowed a greater variety of shapes.