The concept of coherence is important to generating textures that are aesthetically pleasing. Without coherence, the texture overall will look noisy and random, rather than smooth and natural. Coherence gives the generated image some form of structure. The output from the raw hash function is not coherent, but we create a different process to generate coherent noise from the raw noise.
Below, there are some boxes that have procedurally generated textures in them. The left of these boxes has 'raw' noise in it. The other two boxes to the right are two different filters applied onto the same noise field, to generate coherent noise. When zoomed all of the way 'out', these fields will all look similar, but when zoomed in, the fields have their own characteristics.
Some stuff to note:
Interpolated noises create a field that is coherent, even at non-integer offsets. the noise field generated by the raw function has wildly different values at possible point.
The 'Raw Noise' block is generated by this function, applying using the hash function from the previous page:
float hash2d(float x, float y) { return hash(x + 1333.0 * y); }
float lerp(float a, float b, float x) { return a + (b-a) * x; }
This function is very commonly used in all sorts of applications. It can be used for animation, for smoothing images, mixing colors, and anything else that can be done by taking two points, and moving to some position between them. There are also variations on this idea, that interpolate between values in a more smooth fashion.
float coserp(float a, float b, float x) { float ft = x * 3.1415927; float f = (1 - cos(ft)) * .5; return a + (b-a) * f; } float smoothstep(float a, float b, float x) { float t = saturate((x - a)/(b - a)); return t*t*(3.0 - (2.0*t)); }Note: the saturate function clamps a value between 0 and 1.
As can be seen from the above graphs, lerp is just a line between two points. Cosine interpolation and 'smoothStep' look roughly the same, but have slightly different outputs. The difference is in their implementations. Depending on how the cosine function is implemented in the environment (lookup table, crunching a polynomial, or an actual evaluation), the performance of the function may change. Typically the 'smoothstep' interpolation function is used.
The function can then be applied to interpolate between output of a hash function at given points.
Consider the following code:
//Coherent, 2d noise function float noise2d(float2 v) { float ix = floor(v.x); //Integer X float fx = v.x-ix; //Fractional X float iy = floor(v.y); //Integer Y float fy = v.y-iy; //Fractional Y float v1 = hash2d(ix , iy ); //Sample float v2 = hash2d(ix+1, iy ); //4 integer points float v3 = hash2d(ix , iy+1); //around (x,y) float v4 = hash2d(ix+1, iy+1); float i1 = smoothstep(v1, v2, fx); //Interpolate across x axis float i2 = smoothstep(v3, v4, fx); return smoothstep(i1, i2, fy); //Interpolate across y axis }
This is a 2-dimensional interpolation. We sample 4 points (at integer positions). Then we apply an interpolation function between the sampled values, and the fractional position of the target.
Here is a graphical representation of what this looks like.
All of these features together finally give us a nice coherent noise function. Here are the properties that any function would need to be coherent noise:
A type of smooth, pseudorandom noise, generated by a coherent-noise function, with 3 main properties:
This is just one of many possible noise functions- Coherent noise can be created in a few different ways. Depending on how the noise function is constructed, the resulting noise can look very different. There are also ways to create noise that doesn't degrade as quickly due to floating point precision loss, such as spiral noise (that makes heavy use of sine and cosine functions at different phases).
Depending on the construction of the noise, it can have a different look, tiling period, and speed of calculation. It may also degrade at a different rate, and need to be looked at at different scales. Here is a comparison of some of the looks and degredation patterns of different kinds of noise.
Degredation of noise happens for many reasons, but is present because graphics hardware is just not powerful enough to calculate noise functions when the input positions get too far from the origin. The typical culprit is floating point rounding errors. This happens earlier with processes that use both large and small numbers together, but eventually happens to every noise function. There are ways to prevent, or minimize the amount of degredation present, but degredation is pretty much a given for any noise function. The specifics of why and how noise degrades depends on the specifics of the implementation of the noise, and the hardware it is run on.