Navigation menu |
A portable, open-source, coherent noise-generating library for C++ |
|||||||||||||||||||||
Generating coherent noiseThis page describes the construction of a function that generates one-dimensional coherent noise. First, it describes the construction of a linear coherent-noise function. Next, it describes several improvemenrs to that function. Internally, libnoise uses a three-dimensional version of this improved function to generate coherent noise. Table of contentsConstructing the linear coherent-noise functionInteger-noise functionThe basis for all coherent-noise functions is the integer-noise function. An integer-noise function outputs a pseudorandom value given an n-dimensional input value; each dimension of this value must be an integer. This function is very similar to the pseudorandom functions provided by most computer languages, such as C's rand(). rand() returns an integer value that ranges from 0 to RAND_MAX, while an integer-noise function — when given an n-dimensional input value — returns a floating-point value that ranges from -1 to +1. An integer-noise function has three important properties:
Below is the code for a one-dimensional integer-noise function: double IntegerNoise (int n) { n = (n >> 13) ^ n; int nn = (n * (n * n * 60493 + 19990303) + 1376312589) & 0x7fffffff; return 1.0 - ((double)nn / 1073741824.0); } These large integers are primes. These integers may be modified as long as they remain prime; non-prime numbers may introduce discernible patterns to the output. An n-dimensional integer-noise function has n arguments. That function combines the n values in various ways. The following graph shows the output of a one-dimensional integer-noise function n(x):
Note that the integer-noise function is a discrete function; it is defined only for integer values. Continuous integer-noise functionThe next step is to modify the integer-noise function so that it is continuous rather than discrete. This allows the function to accept floating-point input values. A quick-and-dirty way to build a continuous integer-noise function is to round all coordinates of the n-dimensional input value to their nearest integers, then pass this rounded input value to the discrete integer-noise function. The following graph shows the output of a one-dimensional continuous integer-noise function n(x):
The following two- and three-dimensional textures were generated by two- and three-dimensional versions of this continuous integer-noise function:
Note that the output of this continuous integer-noise function has harsh transitions. This can be fixed using interpolation. Interpolating the integer noiseGenerating coherent noise requires a function that outputs a smooth transition between the integer-noise values. This is done by interpolating these integer-noise values. Linear interpolation is the simplest interpolation function. This function, called lerp in computer graphics circles, outputs a linear blend between two values a and b given a weight value w. This function has the following formula: lerp(a, b, w) = a * (1 - w) + b * w The weight is a value between 0 and 1. Weights towards 0 bias the output value towards a. Weights towards 1 bias the output value towards b. This interpolation function makes it possible to construct a coherent-noise function. The following function generates one-dimensional coherent noise given an input value of x: double CoherentNoise (double x) { int intX = (int)(floor (x)); double n0 = IntegerNoise (intX); double n1 = IntegerNoise (intX + 1); double weight = x - floor (x); double noise = lerp (n0, n1, weight); return noise; } Note:
In effect, linear interpolation connects the integer-noise values with straight lines. The following graph shows the output of a one-dimensional coherent-noise function n(x):
This function now passes all the tests for a coherent-noise function:
The following two- and three-dimensional textures were generated by two- and three-dimensional versions of this coherent-noise function:
Note that these textures now have a smooth appearence. They are also starting to resemble "natural" materials. A flaw with the linear coherent-noise functionSo far, this linear coherent-noise function has one major flaw: the textures it generates contain creasing artifacts at integer boundaries. The flaw is especially apparent when this function is used to generate a bump-map texture, as seen in the following image:
The creasing artifacts in this bump-map texture are even more visible. This linear coherent-noise function creates these artifacts because this function's derivative — the rate of change of the output value — is undefined at integer boundaries. To correct this flaw, two changes need to be made to this coherent-noise function. These changes are described in the next section. Improving the linear coherent-noise functionThe linear coherent-noise function (using linear interpolation) produces poor-quality textures because of the creasing artifacts. These artifacts can be removed by using a better interpolation function. Improved interpolation functionsUsing a non-linear interpolation function will remove the discontinuities from the deriviative of the coherent-noise function. This can be done by mapping the weight to an S-curve. An S-curve is a function that generates a smooth S-shaped symmertic curve between 0 and 1. A cubic S-curve has the formula s(x) = -2x^{3} + 3x^{2}. The following graph shows the output of a cubic S-curve function:
To add the cubic S-curve to the coherent-noise function, modify the function as follows: double CoherentNoise (double x) { int intX = (int)(floor (x)); double n0 = IntegerNoise (intX); double n1 = IntegerNoise (intX + 1); double weight = x - floor (x); // double noise = lerp (n0, n1, weight); double noise = lerp (n0, n1, SCurve (weight)); return noise; } (Note that after applying the S-curve to the weight, the range of weight values do not change; they remain between 0 and 1.) Definition note: For the rest of this page, the term cubic coherent-noise function refers to a coherent-noise function that uses a cubic S-curve. The following graph shows the output of a one-dimensional cubic coherent-noise function n(x) next to the graph of a one-dimensional linear function n(x) for comparison:
This graph does not appear to differ much from the graph of a linear coherent-noise function. However, when two- and three-dimensional versions of this function are used to generate two- and three-dimensional noise textures, the improvement is apparent:
The creasing artifacts are now gone. However, a bump-map texture tells a different story:
The bump-map texture produced by cubic coherent-noise function has less-visible creasing artifacts than the linear bump-map texture, but they are still present. These artifacts appear because the second derivative of this function is undefined at integer boundaries. Again, these artifacts can be removed by going to a higher-order function. Replacing the cubic S-curve function with a higher-quality (but slower) quintic S-curve function will take care of this. A quintic S-curve function has the formula s(x) = 6x^{5} - 15x^{4} + 10x^{3}. Definition note: For the rest of this page, the term quintic coherent-noise function refers to a coherent-noise function that uses a quintic S-curve. The following graph shows the output of a one-dimensional quintic coherent-noise function n(x) next to the graph of a one-dimensional linear function n(x) for comparison:
Once again, this graph does not appear to differ much from the graph of a linear coherent-noise function. Even the two- and three-dimensional textures generated by two- and three-dimensional versions of this function do not differ much from the cubic version, as seen in the following images:
However, a bump-map texture generated by this quintic coherent-noise function is considerably better than the cubic version.
This quintic coherent-noise function produces vastly improved textures over the linear coherent-noise function, but one flaw remains: the light and dark areas on these textures are noticably aligned along a grid. This is because the local minimum and maximum values of this function always occur at integer boundaries, causing 'plateaus' to form at these boundaries. Gradient coherent-noise functionTo improve this coherent-noise function, assign random gradient vectors (slopes) at integer boundaries and interpolate these vectors instead of the integer-noise values. Now that the integer-noise values are no longer interpolated, these values can be set to zero. A coherent-noise function with these modifications generates coherent noise that has a much higher quality than the output of the original linear function. These gradient vectors have a magnitude of 1. A gradient coherent-noise function generates a pseudorandom gradient vector by using the integer-noise function to randomly select a vector from a pool of precomputed vectors. The following graph shows the output of a one-dimensional gradient coherent-noise function n(x):
When using two- or three-dimensional versions of this gradient coherent-noise function to generate two- or three-dimensional textures, notice that the light and dark areas are no longer aligned to a grid; they are "pushed around" in some random direction by the pseudorandom gradient vectors. These textures have a nice, natural "splotchy" appearence, as shown in the following images:
Here is a two-dimensional bump-map texture generated by this gradient coherent-noise function next to the texture generated by the original linear function:
libnoise uses a three-dimensional version of this gradient coherent-noise function to generate coherent noise. A single coherent-noise function is rarely used alone; it is often combined with other coherent-noise functions with varying frequencies and amplitudes. This combination of functions, known as Perlin noise, is often used to generate realistic natural textures and terrain. |
||||||||||||||||||||||
© 2003-2005 Jason Bevins |