Perlin Noise
What is Perlin Noise?
If you would like a first introduction to Perlin noise, please click here to view an article I wrote for The Bubble, a student-run newspaper at Durham University, for public reading and understanding. Or feel free to keep reading for a more mathematical-based description with less background.
To start with I will clear up a common misconception about the noise I am on about here. Sadly I don't mean a sweet melodic tune but instead a scalar field of values ranging from 1 to -1.
A scalar field is a function that assigns a value to every point in a space. For two a two-dimensional scalar field imagine a particular height above a point in the plane. Written mathematically, ∀ P = (x1, x2, ..., xn) ∈ ℝn apply the function ϕ: ℝn → ℝ. The value of a scalar field at a point is given by ϕ(P).
Perlin noise is a gradient (smoothed) noise function developed by Ken Perlin, commonly used in computer graphics to generate natural-looking textures and effects. It produces smoothly interpolated random values, making it ideal for simulating organic forms such as clouds, terrain, and waves. Unlike classic random noise, Perlin noise offers coherent, continuous patterns that avoid harsh transitions. To learn more, visit the Perlin noise Wikipedia page, but I shall do my best to explain it as we go.
The definition I have just given tells us the scalar value at any real-valued point. However, as we will see to generate our noise we use a discrete grid of points. This means that we adapt our definition to a finite set of equally spaced-out points.
Below are a series of sketches, some will animate if clicked on and others will update when the parameters are changed, please have a play with them!
The Algorithm
Grid definition
We shall start off by looking at the 2d instance of Perlin noise as I think it is the most intuitive. To set up our grid all we need are a number of columns, rows and resolution. The resolution squared is the number of pixels that will be in each cell of our grid.
- Columns:
- Rows:
- Resolution
Here we only use small numbers to clearly demonstrate the algorithm but in practice, our resolution, especially, would be higher.
Explaining the calculations
Random noise cells
To give a point of reference this is what random noise looks like. This should hopefully be remencient of TV static. Older TV screens would 'go static' due to interference.
That is not what we are doing here, the premise we are interested in is when a TV struggles to find a signal it amplifies what it does have. This is to hopefully have some clarity shown for a poor signal. However, the TV couldn't differentiate poor signals from no signals. This would lead it to amplify whatever data it was receiving which was 'noise' with no distinguishable shape, pattern or consistency over time.
Random noise cells
To give a point of reference this is what random noise looks like. This should hopefully be remencient of TV static. Older TV screens would go static due to interference.
That is not what we are doing here, the premise we are interested in is when a TV struggles to find a signal it amplifies what it does have. This is to hopefully have some clarity shown for a poor signal. However, the TV couldn't differentiate poor signals from no signals. This would lead it to amplify whatever data it was receiving which was 'noise' with no distinguishable shape, pattern or consistency over time.
Perlin noise cells
The motivation of Perlin noise is essentially to get a smooth version of the random noise above. To achieve this Kevin Perlin devised the following algorithm to calculate the value of each cell iteratively. To make it easier to understand we will just look at a 1 by 1 grid with a resolution of 5.
- Create a vector with length 1 at each corner of our cells in a random direction (shown in red).
- Create a vector from the 4 corners of the cell to the centre of our pixel (shown in blue).
- For each pairing of red and blue vectors originating from the same cell corner, then compute their dot product.
- Label these:
top_left, top_right
andbottom_left, bottom_right
.
1. x = the pixel's horizontal position between 0 and 1
2. y = the pixel's vertical position between 0 and 1
3. top_interp = linInterp(x, 0, 1, top_left, top_right)
4. bottom_interp = linInterp(x, 0, 1, bottom_left, bottom_right)
5. perlinNoise = linInterp(y, 0, 1, top_interp, bottom_interp)
Calculating
perlinNoise
, this is our final value for this pixel's noise in the range of 0 to 1.
- Columns:
- Rows:
- Resolution:
- Grid Toggle:
Visualisations
Perlin noise in 2 dimensions
Something I omitted from my explanation of the calculations above is that we also apply a smoothing function to our cell's position (x,y) ∈ [0,1]2. This reduces the harsh transition from one cell to the next. We use the following function.
Perlin noise in 2 dimensions
Something I omitted from my explanation of the calculations above is that we also apply a smoothing function to our cell's position (x,y) ∈ [0,1]2. This reduces the harsh transition from one cell to the next. We use the following function.
Tick this to see what it looks like with no fade function applied (f(x) = x).
Perlin noise in 1 dimension
Here we follow the algorithm for Perlin noise in 1 dimension. We plot its value on the y-axis for each time step we take along the x-axis. This creates a smooth-looking curve from values mapped from -1 to 1 onto the height of the canvas through interpolation. You can get a similar effect if you set the number of rows and resolution to 1 here with "Perlin noise" selected.
Perlin noise in 3 dimension
Here we show that Perlin noise can be expanded to three dimensions visualising it akin to panning through a block of marble. Each slice will have a similar pattern to it's neighbour but will also be different.
Perlin noise in 3 dimension
Here we show that Perlin noise can be expanded to three dimensions visualising it akin to panning through a block of marble. Each slice will have a similar pattern to it's neighbour but will also be different.
Perlin World
In this sketch, I have used Perlin noise for almost every aspect! I shall explain each of these below.
- The mountains and dry sand are stationary in the animation but hopefully, you can see, that they resemble the initial line drawn in Perlin noise in 1 dimension just with some tweaking. Then it is almost identical with the wet sand and ocean layers where we have some offset value which increases over time meaning the noise space sampled is shifted slightly values giving the ocean effect.
- The clouds are generated and move by, you guessed it, Perlin noise. To get the bubbly effect of the clouds I overlapped circles of varying radii. I determined the centres' of the circles by creating an oval shape using
sin
andcos
then putting a circle at random points around the centre of the cloud. - The trees' position and "wind" are also determined by... Perlin noise. I use it to them neatly on the dry sand and have every branch of a particular tree bend in an organic manner to give a wind effect. The height and branch depth of the trees are random. Here I explain how the tree drawing works.
- The sun has a breathing motion to it where its radius is determined (for the last time) by Perlin noise.
Recursive Tree
As I included this tree in my Perlin world sketch, I thought I would explain how it works here (even though I don't use Perlin noise!).
The program works by recursively (repeatedly until a point) performing almost the exact same action. It starts at the bottom of the screen and moves forward a line of length len
. Then it updates the line length as len <- len / 3
, then turns to the left and draws a line, then again but to the right of the centre. It then moves to the left and aligns itself, then repeats these steps with the new length of the shorter lines.
Perlin Flow Field
In this sketch, I use Perlin noise to determine the angle of vectors at the vertices of a grid. This is much like the idea used to generate the values of Perlin noise, but then applied to a different grid.