Arbitrary-dimensional continuous noise that maintains most of the same style even as the dimensionality gets fairly
high. If you know what dimension of noise you need, and it's covered by
FoamNoise
(meaning it's 2D, 3D, 4D,
or 6D noise), then using FoamNoise will give you a significantly faster version of approximately the same algorithm.
If your noise has an unknown dimension count, this won't really work either, since PhantomNoise needs to do some
preparation and allocation for a specific dimension in its constructor. But, if you know what the range of dimensions
is, and the lowest is at least 2D, you can make one PhantomNoise per dimension easily enough.
The algorithm this uses is surprisingly simple. To produce N-D noise, it makes N+1 calls to N-D value noise, with
each call using a different rotation of the same size of grid. It "domain warps" value noise calls after the first,
adding the previous value noise result to one coordinate of the next value noise call. To get a PhantomNoise result,
it averages all the value noise calls and curves the output range so it doesn't get more biased toward 0 with higher
dimensions (which would happen with a pure average of a rising number of variables). The curving here uses the
MathExtras.barronSpline(double, double, double)
method for adjustable bias/gain; it looks
like this for 2D,
like this for 3D, and
like this for 4D.
There's some preparation this does in the constructor, which eliminates the need for allocations during noise
generation. For N-D PhantomNoise, this makes N+1 double arrays, one for each rotation for a value noise call, and
each rotation array has N items. The rotations match the vertices of an N-simplex, so a triangle in 2D, a tetrahedron
in 3D, etc. It also stores two working-room arrays, each with N+1 double items, two frequently-edited int arrays of
the floors of doubles it's using, and of modified versions of those floors to be hashed (each with N+1 items), and
interestingly, a
CrossHash.Curlup
hash functor, seeded in the PhantomNoise constructor.
At higher dimensions, Simplex noise (what
SeededNoise
produces) starts to change how it looks compared to
lower dimensions. PhantomNoise, on the other hand, maintains a fairly consistent blob-like organic look, such as
this 6D PhantomNoise sample. Red and purple mark the highest and lowest
possible values, respectively, and while they appear plenty in PhantomNoise, they are absent in
this 6D SeededNoise sample. There may be differences in how the inputs
are handled between the two samples, but 6D Simplex generally suffers from "the curse of dimensionality" more-so than
PhantomNoise (or Perlin noise like
ClassicNoise
, somewhat surprisingly), with the "curse" affecting the
density of information in higher-dimensional space.
This is marked Beta because it's still pretty slow at higher dimensions, and if some optimization becomes available
here, I'll take it and the output will change from this version.