libnoise logo

A portable, open-source, coherent noise-generating library for C++


Generating coherent noise

This 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.

Constructing the linear coherent-noise function

Integer-noise function

The 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:

  1. Passing in the same input value will always return the same output value.
  2. The output value generated by the input value a and the output value generated by the input value b (a != b) will have no correlation with each other.
  3. The output value will always be within the range -1 to +1.

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):

Integer-noise function graph

Note that the integer-noise function is a discrete function; it is defined only for integer values.

Continuous integer-noise function

The 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):

Continuous integer-noise function graph

The following two- and three-dimensional textures were generated by two- and three-dimensional versions of this continuous integer-noise function:

2D texture generated by a continuous integer-noise function 3D texture generated by a 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 noise

Generating 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:

  • A two-dimensional coherent-noise function uses bilinear interpolation instead of linear interpolation.
  • A three-dimensional coherent-noise function uses trilinear interpolation instead of linear interpolation.

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):

Coherent-noise function graph

This function now passes all the tests for a coherent-noise function:

  1. Passing in the same input value will always return the same output value.
  2. A small change in the input value will produce a small change in the output value.
  3. A large change in the input value will produce a random change in the output value.

The following two- and three-dimensional textures were generated by two- and three-dimensional versions of this coherent-noise function:

2D texture generated by a coherent-noise function 3D texture generated by a 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 function

So 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:

Bump-mapped 2D texture generated by a coherent-noise function

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 function

The 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 functions

Using 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) = -2x3 + 3x2.

The following graph shows the output of a cubic S-curve function:

Cubic S-curve graph

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:

Cubic Linear
Cubic coherent-noise function graph Linear coherent-noise function graph

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:

2D texture generated by a cubic coherent-noise function 3D texture generated by a cubic coherent-noise function

The creasing artifacts are now gone. However, a bump-map texture tells a different story:

Cubic Linear
Bump-mapped 2D texture generated by a cubic coherent-noise function Bump-mapped 2D texture generated by a linear coherent-noise function

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) = 6x5 - 15x4 + 10x3.

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:

Quintic Linear
Quintic coherent-noise function graph Linear coherent-noise function graph

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:

2D texture generated by a quintic coherent-noise function 3D texture generated by a quintic coherent-noise function

However, a bump-map texture generated by this quintic coherent-noise function is considerably better than the cubic version.

Cubic Quintic Linear
Bump-mapped 2D texture generated by a cubic coherent-noise function Bump-mapped 2D texture generated by a quintic coherent-noise function Bump-mapped 2D texture generated by a linear coherent-noise function

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 function

To 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):

Gradient coherent-noise function graph

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:

2D texture generated by a gradient coherent-noise function 3D texture generated by a gradient coherent-noise function

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:

Gradient Original
Bump-mapped 2D texture generated by a gradient coherent-noise function Bump-mapped 2D texture generated by the original coherent-noise 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.