blog.hirnschall.net

Contents

What is Perlin Noise

Perlin noise is a type of gradient noise used in the movie and special effects industry for procedural texture generation. It was developed by Ken Perlin in 1983. He was later awarded an Academy Award for Technical Achievement for creating the algorithm. 

(a) plot of Perlin Noise
(b) plot of random numbers
Figure 1.1: Comparing Perlin Noise to random numbers (p5js random() and noise())

As you can see in fig 1.1 \(noise(x),x\in\mathbb{R}\) is a continuous function. In other words, Perlin noise can be used to generate a more "controlled" form of randomness.

How Gradient Noise Works

To generate the random, continuous function \(noise(x):\mathbb{R}^n\to\mathbb{R}^n\)  we start with a set of random vectors (gradients)

$$g_u\in\mathbb{R}^n,u\in\mathbb{Z}^n.$$

As the name gradient noise implies we now set

$$noise'(u)=g_u$$ $$noise(u)=0.$$

We can now interpolate to find \(noise(x),\forall x\in\mathbb{R}^n\).

Figure 2.1: Resulting interpolant for one-dimensional noise

As you can see in fig. 2.1 the resulting function \(noise(x)\) is zero if \(x=u\). To get a better result we take a linear combination of \(M\) weighted noise functions with different frequencies

$$Noise(x)=\sum_{i=0}^{M-1}a_i\cdot noise(f_i\cdot x)$$

where \(a_i\) is a weight (amplitude) and \(f_i\) is a frequency. These terms are called octaves.

Octaves used in this animation:

Result:

How Value Noise Works

The apparently "often confused with" value noise works in a similar way to gradient noise. We start with a grid of random points

$$g_u\in\mathbb{R}^n,u\in\mathbb{Z}^n.$$

In contrast to gradient noise we demand that

$$noise(u)=g_u.$$ As with gradient noise, we then use the interpolant to calculate \(noise(x),x\in\mathbb{R}^n\). To get a better result we can use a linear combination of \(M\) different octaves (with different weights, \(a_i\) and frequencies \(f_i\)). $$Noise(x)=\sum_{i=0}^{M-1}a_i\cdot noise(f_i\cdot x)$$

Gradiant Noise vs Value Noise

In short: As gradient (Perlin) noise emphasizes frequencies around and above the grid spacing it will, in general, lead to a visually more appealing result.

One problem with value noise can be its random nature. The result might look ugly if too many gridpoints have a similar value.

For more information on gradient noise vs value noise take a look at this answer on StackExchange.

Computational complexity

As we have seen above, to evaluate \(noise(x),x\in\mathbb{R}^n\) (n-dimensional Perlin noise), we have to interpolate between the \(2^n\) nearest grid points. We will later see that this can be done by calculating the dot-product of the gradient assigned to each of these points and \(x\). Afterward, we interpolate linearly between the results and apply an ease curve.

As all of these operations scale with complexity \(\mathcal{O}(n)\) the algorithm scales with complexity \(\mathcal{O}(2^n)\) for \(n\) dimensions.

When to use Perlin Noise?

Perlin noise is typically used to make things look more realistic or live like. As most things in nature do not change instantaneous, using a normal random number generator is not a good option. As we have seen Perlin noise is a continuous ("smooth") function and therefore the result looks much more natural.

For more flexibility, we may use different dimensions of Perlin noise. You can see some example use cases below:

Dimension Visualization Example usage
1 1d Perlin noise can be used to make a straight line look hand-drawn or make movement look more realistic (no instant speed changes, no perfectly straight lines, etc.)
2
2d Perlin noise is often used to generate terrain, textures, or flowfields.
3
3d Perlin noise can be used to generate caves (like those in Minecraft) or for animating textures etc. that use 2d noise

How to use Perlin Noise

Implementation

If the language you are using does not have Perlin noise as a built-in function you can either use Ken Perlin's reference implementation to implement it yourself or check github.com if someone else already did all the work.

If you are using unity you can use Mathf.PerlinNoise(float x, float y).

Using Perlin noise (for animations)

In contrast to \(random()\), \(noise(t)\) will return the same value for a given \(t\) no matter how often we call it. To get different results we increment \(t\). We can control the smoothness by how quickly we increment \(t\).

Figure 3.1: Timesteps [1]

If we need more than one noise value \(x:=noise(t_1)\) and \(y:=noise(t_2)\) we can use a large offset between \(t_1\) and \(t_2\). You can see how this works in fig. 3.2 below:

Figure 3.2: Different values [1]

Mapping the result

Depending on the implementation the noise function will return a value \(x\) in a given intervall\([a,b],a<b\). This is not what we want for most usecases. If we want a value between \(c\) and \(d\) with \(c<d\), we can map the intervall \([a,b]\) to \([c,d]\) using

$$map(x,a,b,c,d) := \frac{(x-a)(d-c)}{b-a}+c.$$

Some languages have this function already implemented (p5js for example).

Terrain generation

Let's start with a triangulation of a plane (fig. 3.3 (a)). We can now use 2d Perlin noise to assign a random z value (height) to each vertex (x,y). To control the smoothness we can divide both x and y by some constant s. We will also map the result of noise(x,y), a value between 0 and 1 (in p5js), to a value between minheight and maxheight to control the min and max height of the terrain we generate. You can see the result in fig. 3.3 (b).

(a) constant height value
(b) height with Perlin noise
Figure 3.3: Terrain generation using Perlin noise
    for(var y =0;y<height/w;++y){
  beginShape(TRIANGLE_STRIP);
  for(var x =0;x<=width/w;++x){
    vertex(x*w,j*w,map(noise(x/s,y/s),0,1,minheight,maxheight));
    vertex(x*10,(y+1)*w,map(noise(x/s,(y+1)/s),0,1,minheight,maxheight));
  }
  endShape();
}
Listing 3.1: Example code to generate terrain with Perlin noise

Animation

Using the example above we'll start with a terrain (or texture) \(T=\{(x,y,noise(x,y)):x\in[a,b],y\in[c,d]\}\). If you are not familiar with this notation, it means the terrain (or texture) \(T\) is a set of points \((x,y)\) and their corresponding noise value \(noise(x,y)\). If we want this terrain (or texture) to change smoothly we can:

Using 3d Perlin noise, we know that \(noise(x,y,t_1)\) will be close to \(noise(x,y,t_2)\), if \(t_1\) is close to \(t_2\). So, we use 2d nosie to create the terrain/texture and animate it using a third dimension. You can see the result below:
(a) using an extra dimension
(b) scrolling
Figure 3.4: Different animation approaches (looped video)

More examples

For more examples take a look at these posts on my blog:

Get Notified of New Articles

Subscribe to get notified about new projects. No spam. No ads. Our Privacy Policy applies.

License

The content published on this page (with exceptions) is published under the CC Attribution-NonCommercial 3.0 Unported License. You are free to share on commercial blogs/websites as long as you link to this page and do not sell material found on blog.hirnschall.net/* or products with said material.

References

Visited on 13.07.2020:
[1] https://natureofcode.com/book/
[2] http://staff.fh-hagenberg.at/burger/publications/reports/2008GradientNoise/Burger-GradientNoiseGerman-2008.pdf
[3] https://math.stackexchange.com/q/184153
[4] https://mrl.nyu.edu/~perlin/noise/
Article by: Sebastian Hirnschall
Updated: 27.08.2020