Class PhantomNoise
java.lang.Object
com.github.yellowstonegames.grid.PhantomNoise
- All Implemented Interfaces:
INoise, Externalizable, Serializable
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
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
At higher dimensions, Simplex noise (what
Noise (meaning it's 2D, 3D, 4D, 5D,
or 6D noise), then using Noise with the Noise.FOAM noise type 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
MathTools.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 Hasher hash functor, seeded in the PhantomNoise constructor.
At higher dimensions, Simplex noise (what
Noise.SIMPLEX 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 Noise.PERLIN, somewhat surprisingly), with the "curse" affecting the
density of information in higher-dimensional space.- See Also:
-
Nested Class Summary
Nested classes/interfaces inherited from interface INoise
INoise.Serializer -
Field Summary
FieldsModifier and TypeFieldDescriptionintHow many dimensions of noise to generate; usually at least 2.protected int[]Stores the floors of each input to valueNoise().com.github.tommyettinger.digital.HasherEffectively, this contains the seed for the noise.protected int[]Passed toHasher.hash(long, float[])after modifications to get a random-seeming int for each vertex on the square-like grid in valueNoise().protected float[]Used with the INoise methods to assign individual arguments into a saved array, rather than allocating a new one.static final PhantomNoisestatic final PhantomNoisestatic final PhantomNoisestatic final PhantomNoisestatic final PhantomNoisestatic final PhantomNoisestatic final PhantomNoiseprotected floatJust1f / (dim + 1f).protected float[]Stores thedim + 1dot-products of each vertex with each input.floatThis should go up linearly with dimension, typically, and is usually 0.825 timesdim.protected float[][]Storesdim + 1vertices, each an array made ofdimfloats.protected float[]Assigned in getNoise() to a rotated version of the input, and used in valueNoise() to get the rotated-grid value noise result. -
Constructor Summary
ConstructorsConstructorDescriptionBuilds a PhantomNoise with the0xFEEDBEEF1337CAFELas its seed, that takes 3 inputs per noise call, and uses0.825f * 3as the sharpness (for contrast).PhantomNoise(long seed, int dimension) Builds a PhantomNoise with the specifiedseed, that takesdimensioninputs per noise call, and uses0.825f * dimensionto determine the sharpness (for contrast).PhantomNoise(long seed, int dimension, float sharpness) Builds a PhantomNoise with the specifiedseed, that takesdimensioninputs per noise call, and uses the givensharpnessto determine contrast. -
Method Summary
Modifier and TypeMethodDescriptioncopy()Creates a copy of this INoise, which should be a deep copy for any mutable state but can be shallow for immutable types such as functions.booleanintGets the maximum dimension supported by this generator, such as 2 for a generator that only is defined for flat surfaces, or 7 for one that is defined up to the highest dimension this interface knows about (7D).intGets the minimum dimension supported by this generator, such as 2 for a generator that only is defined for flat surfaces, or 3 for one that is only defined for 3D or higher-dimensional spaces.floatgetNoise(float... args) floatgetNoise(float x, float y) Gets 2D noise with a default or pre-set seed.floatgetNoise(float x, float y, float z) Gets 3D noise with a default or pre-set seed.floatgetNoise(float x, float y, float z, float w) Gets 4D noise with a default or pre-set seed.floatgetNoise(float x, float y, float z, float w, float u) Gets 5D noise with a default or pre-set seed.floatgetNoise(float x, float y, float z, float w, float u, float v) Gets 6D noise with a default or pre-set seed.floatgetNoise(float x, float y, float z, float w, float u, float v, float m) Gets 7D noise with a default or pre-set seed.floatgetNoise2D(float x, float y) longgetSeed()Gets the current seed of the generator, as a long even if the seed is stored internally as an int.getTag()Returns a typically-four-character String constant that should uniquely identify this INoise as well as possible.booleanReturns true if this generator can be seeded withINoise.setSeed(long)during each call to obtain noise, or false if calling setSeed() is slow enough or allocates enough that alternative approaches should be used.floatnoise(int used, float... args) reassign(long seed, int dimension, float sharpness) Reassigns all members of this class at once, doing the same work the constructor does, but without creating a new PhantomNoise.static PhantomNoiserecreateFromString(String data) voidsetSeed(long seed) Sets the seed to the given long, if long seeds are supported, or(int)seedif only int seeds are supported.stringDeserialize(String data) Given a serialized String produced byINoise.stringSerialize(), reassigns this INoise to have the described state from the given String.Produces a String that describes everything needed to recreate this INoise in full.protected floatvalueNoise(int dim) protected floatMethods inherited from class Object
clone, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, waitMethods inherited from interface INoise
getNoiseWithSeed, getNoiseWithSeed, getNoiseWithSeed, getNoiseWithSeed, getNoiseWithSeed, getNoiseWithSeed, readExternal, writeExternal
-
Field Details
-
hasher
public com.github.tommyettinger.digital.Hasher hasherEffectively, this contains the seed for the noise. -
dim
public int dimHow many dimensions of noise to generate; usually at least 2. -
sharpness
public float sharpnessThis should go up linearly with dimension, typically, and is usually 0.825 timesdim. It can be raised to make the noise more starkly black and white, or lowered to have more mid-gray values. -
inverse
protected float inverseJust1f / (dim + 1f). -
working
protected transient float[] workingAssigned in getNoise() to a rotated version of the input, and used in valueNoise() to get the rotated-grid value noise result. Hasdim + 1elements, but one is effectively a changing "seed-like" input to differentiate each rotation. -
points
protected transient float[] pointsStores thedim + 1dot-products of each vertex with each input. -
input
protected transient float[] inputUsed with the INoise methods to assign individual arguments into a saved array, rather than allocating a new one. -
vertices
protected transient float[][] verticesStoresdim + 1vertices, each an array made ofdimfloats. These are the vertices of a simplex (an analogue to a triangle or tetrahedron) indimdimensions. -
floors
protected transient int[] floorsStores the floors of each input to valueNoise(). These are used to compute cubic interpolations ofworkingto smooth the value noise. This hasdim + 1items, becauseworkingstores an extra float to differentiate rotations. -
hashFloors
protected transient int[] hashFloorsPassed toHasher.hash(long, float[])after modifications to get a random-seeming int for each vertex on the square-like grid in valueNoise(). Hasdim + 1items, becauseworkingstores an extra float to differentiate rotations. -
instance2D
-
instance3D
-
instance4D
-
instance5D
-
instance6D
-
instance7D
-
instance8D
-
-
Constructor Details
-
PhantomNoise
public PhantomNoise()Builds a PhantomNoise with the0xFEEDBEEF1337CAFELas its seed, that takes 3 inputs per noise call, and uses0.825f * 3as the sharpness (for contrast). -
PhantomNoise
public PhantomNoise(long seed, int dimension) Builds a PhantomNoise with the specifiedseed, that takesdimensioninputs per noise call, and uses0.825f * dimensionto determine the sharpness (for contrast).- Parameters:
seed- may be any long; if the seed is the same and the same inputs are given, the noise will be identicaldimension- how many inputs it will take each timegetNoise(float...)is called
-
PhantomNoise
public PhantomNoise(long seed, int dimension, float sharpness) Builds a PhantomNoise with the specifiedseed, that takesdimensioninputs per noise call, and uses the givensharpnessto determine contrast.- Parameters:
seed- may be any long; if the seed is the same and the same inputs are given, the noise will be identicaldimension- how many inputs it will take each timegetNoise(float...)is calledsharpness- typically about0.825f * dimension; higher makes more contrasting noise
-
-
Method Details
-
reassign
Reassigns all members of this class at once, doing the same work the constructor does, but without creating a new PhantomNoise. This can also avoid recreating arrays ifdimis equal todimension.- Parameters:
seed- may be any long; if the seed is the same and the same inputs are given, the noise will be identicaldimension- how many inputs it will take each timegetNoise(float...)is calledsharpness- typically about0.825f * dimension; higher makes more contrasting noise- Returns:
- this PhantonNoise, after modification
-
getTag
Description copied from interface:INoiseReturns a typically-four-character String constant that should uniquely identify this INoise as well as possible. If a duplicate tag is already registered andINoise.Serializer.register(INoise)attempts to register the same tag again, a message is printed toSystem.err. The default implementation returns the String(NO), which is essentially an invalid tag, meant to indicate that this was not fully implemented. Implementing this is required for any usage of Serializer. -
stringSerialize
Description copied from interface:INoiseProduces a String that describes everything needed to recreate this INoise in full. This String can be read back in byINoise.stringDeserialize(String)to reassign the described state to another INoise. The syntax here should always start and end with the`character, which is used byINoise.stringDeserialize(String)to identify the portion of a String that can be read back. The`character should not be otherwise used unless to serialize another INoise that this uses.
If you useBaseto produce String representations for numeric fields in an INoise,Base.BASE10is strongly recommended; in most cases, you can just use string concatenation with the fields separated by the tilde character,"~". For printing a floatfieldwith Base to a StringBuildersb, useBase.BASE10.appendGeneral(sb, field).
The default implementation throws anUnsupportedOperationExceptiononly. INoise classes do not have to implement any serialization methods, but they aren't serializable by the methods in this class or inINoise.Serializerunless they do implement this,INoise.getTag(),INoise.stringDeserialize(String), andINoise.copy().- Specified by:
stringSerializein interfaceINoise- Returns:
- a String that describes this INoise for serialization
-
stringDeserialize
Description copied from interface:INoiseGiven a serialized String produced byINoise.stringSerialize(), reassigns this INoise to have the described state from the given String. The serialized String must have been produced by the same class as this object is.
Any class that implementsINoise.stringSerialize()should also implement this method in a compatible way. Using base-10 is expected for most uses. UsingBase.BASE10.readFloat(data, start, end)may be useful to parse only the part of data between start and end.
The default implementation throws anUnsupportedOperationExceptiononly. INoise classes do not have to implement any serialization methods, but they aren't serializable by the methods in this class or inINoise.Serializerunless they do implement this,INoise.getTag(),INoise.stringSerialize(), andINoise.copy().- Specified by:
stringDeserializein interfaceINoise- Parameters:
data- a serialized String, typically produced byINoise.stringSerialize()- Returns:
- this INoise, after being modified (if possible)
-
recreateFromString
-
copy
Description copied from interface:INoiseCreates a copy of this INoise, which should be a deep copy for any mutable state but can be shallow for immutable types such as functions. This almost always just calls a copy constructor.
The default implementation throws anUnsupportedOperationExceptiononly. Implementors are strongly encouraged to implement this in general, and that is required to use an INoise class withINoise.Serializer. -
valueNoise
protected float valueNoise(int dim) -
valueNoise2D
protected float valueNoise2D() -
getNoise
public float getNoise(float... args) -
noise
public float noise(int used, float... args) -
getNoise2D
public float getNoise2D(float x, float y) -
equals
-
getMinDimension
public int getMinDimension()Description copied from interface:INoiseGets the minimum dimension supported by this generator, such as 2 for a generator that only is defined for flat surfaces, or 3 for one that is only defined for 3D or higher-dimensional spaces.- Specified by:
getMinDimensionin interfaceINoise- Returns:
- the minimum supported dimension, from 2 to 7 inclusive
-
getMaxDimension
public int getMaxDimension()Description copied from interface:INoiseGets the maximum dimension supported by this generator, such as 2 for a generator that only is defined for flat surfaces, or 7 for one that is defined up to the highest dimension this interface knows about (7D).- Specified by:
getMaxDimensionin interfaceINoise- Returns:
- the maximum supported dimension, from 2 to 7 inclusive
-
hasEfficientSetSeed
public boolean hasEfficientSetSeed()Description copied from interface:INoiseReturns true if this generator can be seeded withINoise.setSeed(long)during each call to obtain noise, or false if calling setSeed() is slow enough or allocates enough that alternative approaches should be used. You can always call setSeed() on your own, but generators that don't have any seed won't do anything. Generators that return false for this method will generally behave differently when comparing howINoise.getNoiseWithSeed(float, float, long)changes the seed and how setSeed() does.- Specified by:
hasEfficientSetSeedin interfaceINoise- Returns:
- whether
INoise.setSeed(long)should be efficient to call in everyINoise.getNoiseWithSeed(float, float, long)call
-
getSeed
public long getSeed()Description copied from interface:INoiseGets the current seed of the generator, as a long even if the seed is stored internally as an int. This must be implemented, but if the generator doesn't have a seed that can be expressed as a long (potentially usingBitConversion.floatToIntBits(float)), this can just return0. -
setSeed
public void setSeed(long seed) Description copied from interface:INoiseSets the seed to the given long, if long seeds are supported, or(int)seedif only int seeds are supported. If this generator cannot be seeded, this should do nothing, and should not throw an exception. If this operation allocates or is time-intensive, thenINoise.hasEfficientSetSeed()should return false. That method is checked inINoise.getNoiseWithSeed(float, float, long), and if it returns false, the noise call will avoid calling setSeed(). You can always at least try to set the seed, even if it does nothing or is heavy on performance, and doing it a few times each frame should typically be fine for any generator. In the case this is called thousands of times each frame, checkINoise.hasEfficientSetSeed(). -
getNoise
public float getNoise(float x, float y) Description copied from interface:INoiseGets 2D noise with a default or pre-set seed. -
getNoise
public float getNoise(float x, float y, float z) Description copied from interface:INoiseGets 3D noise with a default or pre-set seed. -
getNoise
public float getNoise(float x, float y, float z, float w) Description copied from interface:INoiseGets 4D noise with a default or pre-set seed. -
getNoise
public float getNoise(float x, float y, float z, float w, float u) Description copied from interface:INoiseGets 5D noise with a default or pre-set seed.- Specified by:
getNoisein interfaceINoise- Parameters:
x- x position; can be any finite floaty- y position; can be any finite floatz- z position; can be any finite floatw- w position; can be any finite floatu- u position; can be any finite float- Returns:
- a noise value between -1.0f and 1.0f, both inclusive
-
getNoise
public float getNoise(float x, float y, float z, float w, float u, float v) Description copied from interface:INoiseGets 6D noise with a default or pre-set seed.- Specified by:
getNoisein interfaceINoise- Parameters:
x- x position; can be any finite floaty- y position; can be any finite floatz- z position; can be any finite floatw- w position; can be any finite floatu- u position; can be any finite floatv- v position; can be any finite float- Returns:
- a noise value between -1.0f and 1.0f, both inclusive
-
getNoise
public float getNoise(float x, float y, float z, float w, float u, float v, float m) Description copied from interface:INoiseGets 7D noise with a default or pre-set seed.- Specified by:
getNoisein interfaceINoise- Parameters:
x- x position; can be any finite floaty- y position; can be any finite floatz- z position; can be any finite floatw- w position; can be any finite floatu- u position; can be any finite floatv- v position; can be any finite floatm- m position; can be any finite float- Returns:
- a noise value between -1.0f and 1.0f, both inclusive
-