Home Projects

Perlin noise

The purpose of this article is to describe the principle behind Perlin Noise the most intuitively possible, and explain how to implement it in practice. There are many ways to code Perlin Noise (some more optimized than others), but this article will focus on a simple and easily readable implementation.  At the end, you will be able to produce a grayscale image generated using Perlin Noise, such as shown above. Of course, the algorithm can be used to produced other types of generated content, such as height maps. The hexagon map that you can see above was generated using 2 Perlin Noise maps: one for the elevation, and one for the forest.

The intuition behind Perlin Noise

The Perlin Noise algorithm can be summarized into the following steps:

• First, we divide our map into a grid of cells. At each intersection of the grid, we assign a gradient vector that will represent the slope of our final map this point. The figure below represents the grid of gradient vectors at each corner of our cells. • To get the value of our Perlin Noise at a specific point, we first find the cell in which the point is contained.
• We then do some math magic: we compute the scalar product between each corner of the cell and the distance vector between this corner and our point.
• We interpolate the result of the 4 scalar products based on the position of our point in the cell.

This summary may sound complicated, but each step is detailed with code in the following sections. I give this summary so that you can have an overview of the progression of the algorithm.

The implementation

The goal of our algorithm is to produce a grayscale texture. For this, we will produce a 2-dimensional array of grayscale value ([0, 255] where 0 is black, and 255 is white), where each value represents a pixel, that we will fill thanks to Perlin Noise.

We will implement the two functions get_perlin_vectors() and get_perlin_noise_grid() in the following sections. The first function will generate the grid of vectors used by the second function to compute the value of the point. Note that get_perlin_noise_grid() returns a value in the range [-1, 1], so we scale it so that it corresponds to a grayscale value in the range [0, 256].

Generate the gradient vectors

The goal is to generate a grid of cells, with a gradient vector at each intersection of the grid. We will compute the number of cells based on the size of the final image, and the size of a cell (which we will call period in the code).

You can do a quick sanity check to see if the formula for grid_width and grid_height are correct. For example, for a width of 7 and a period of 3, we will need 4 gradient vectors: one at 0, 3, 6 and 9.

There are multiple ways to implement the random_vector() function. For this article, we will simply generate a unit vector pointing in a random direction.

Note that I could have made the amplitude of the vector random too. This is up to you (and depending on the desired result) to choose which kind of method you want.

Retrieve the value at a certain point

Now comes the most mathematically intensive part. We are searching to get the Perlin Noise value at a certain coordinate (x, y). The entirety of the code presented in this section is placed inside a function:

We first determine which cell the point is in. For example, if we have a period of 3, and we want the Perlin value for the coordinate (3, 7), those calculations will yield cell_x = 1, cell_y = 2, relative_x = 0, relative_y = 0.33.

Before we proceed, we must apply a small smooth function over the relative coordinates. This will help round the edges of our Perlin noise, specially at the border of cells. In the interactive application at the end of the section, you will be able to toggle this application of the fade function to see its impact on the final result.

We will now fetch the 4 gradient vectors at the 4 corners of our cell.

We will now compute the 4 scalar products (also called dot product). The first 2 arguments represent the coordinates of the gradient vector of a corner and the last 2 represent the distance vector starting from this corner and to the point.

We now linearly interpolate (or lerp) the final value based on the 4 initial contributions. This means that we will give more weight to the top-left gradient if the point is closer to the top-left corner (this translates to a low relative_x and thus a high 1 - relative_x).

To get a final_value in the range [-1, 1], we must divide the final value by sqrt(2) / 2. Indeed, final_value will take its maximum value when the point is the center of the cell, and when the 4 corner gradients are directly pointing toward the point. In this specific configuration, the dot product will yield its maximum value which is the product of the amplitudes of the 2 vectors. Since our gradient vector always has an amplitude of 1 (see the random_vector function), and the distance between the center and the corner of a square whose sides are 1 in length is sqrt(2) / 2, the maximum value is 1 * sqrt(2) / 2 = sqrt(2) / 2. The lerp operations won’t influence this maximum value.

Here is a small simulator that implements the algorithm given above.

 Seed: Period: Smoothed (fade): Overlay:

And here is the entire code in one block.

The Perlin Noise at the moment doesn’t look good. To have something more visually appealing, we will need to implement Octaved Perlin Noise.

It is though interesting to see the impact of the parameters on the result. For example, if you set the period to 128 and disable smoothing, you will clearly the 16 cells (4 by 4) that makes the final image. By toggling the overlay, you can see the influence of the vectors on the color of the cell. The area in the direction of a gradient vector will generally be white, while the area “behind” a gradient vector will generally black.

Juxtaposing multiple octaves (or periods)

To get a good looking noise like shown in the first picture of this article, you must juxtapose multiple Perlin Noise with different periods. For example, we can take the noise with a period of 128, then 64, then 32, etc…

You may sometime see the term Octaved Perlin Noise when referring to this technique. In music, an octave is the interval between a period and its half (or a frequency, and its double).

To implement this, we simply re-generate multiple Perlin Noise grid with different periods, and linearly add them to form a merged grid. The code detailed in this section is contained in the following function:

We want to generate and merge octaves different grids. The first one will have a period of start_period, the second one a period of start_period / 2, the third one a period of start_period / 4 and so on.

We also want the small-period grids to have a lower impact on the final result than the big-period grids. Indeed, if you use your Perlin Noise to generate a heightmap (such as in Minecraft, for example), you want to use bigger periods to generate large mountains or large oceans, and smaller periods to generate small hills or pools. Note that you can always choose not to do that, depending on the result you want.

For this article, we will simply divide by 2 the range of the values for each octave. The first grid (period 128) will then yield values in the range [-1, 1], the second grid (period 64) in [-0.5, 0.5], the third grid (period 32) in [-0.25, 0.25] and so on. We say that we attenuate the higher octaves with a factor of 0.5. Note that we can generalize, and say that we attenuate the i-th octave by a factor of 0.5^i.

Note that since we add multiple grids together, we increase the maximum value that may be generated. Indeed, if we add 3 octaves together, with an attenuation of 0.5, final_grid may contain values up to 1 + 0.5 + 0.25 = 1.75. To be consistent with the first algorithm, we will compute this maximum value (max_value in the code) and divide every value of our grid by it, before finally returning the merged grid.

 Seed: Start Period: Octaves: Attenuation:

Start by setting a period of 128 and only 1 octaves, you will see a traditionnal Perlin Noise like we did in the first part of this article. You can then increase the number of octaves and see the impact of each added Perlin Noise grid on the final image.

The entire code.

Other uses

We saw how to generate a grayscale texture using Perlin Noise, but it can be used for many purposes. To generate this map (thanks to @CuddlyColin for the assets), I used 2 Perlin Noise grids: one for the elevation to define water, land and mountain areas, and another one for the humidity to define land, forests and oasis. I had to tweak the numbers (periods, attenuation) a lot to get a good and natural map, this is why it is important to understand what impact each parameters has on the final result. You can for example guess that the period used for the Perlin noise will give a rough estimate of the size of an ocean or a forest in the final map. This image (Source) shows how you can Perlin Noise to simulate handwriting. The author generated a one-dimension Perlin Noise grid, and then, using a perfect circle as base, used it as an offset for the circle. Depending on the value on the noise, the circle will sometimes have a bigger radius, sometimes a lower radius, simulating a hand drawn circle.