This article goes into depth around four different types of noise generation algorithms. We cover the implementation as well as performance information for Perlin, Simplex, Wavelet, and Worley noise. This is an analysis of the implementation and the pro’s and cons for each.
Noise is a random disturbance in a sample which is not part of an expected result. Think of audio signals on images that you take with your Digital SLR. The noise is the pieces of information that are not desirable such as hiss or speckled spots on low light photos. In these instances this extra information is usually undesirable but in some application this noise makes things more natural and realistic when using random information.
There are many different applications of noise in game development from terrain generation, procedural textures, to artificial intelligence and many more. Noise is surprisingly interesting and is not necessarily just random numbers. Its about how are those numbers distributed how to they relate to previous information. This can be quite a complex topic and in this blog post I will be going through the following types of noise and providing source code and libraries you can leverage to create applications. The types I am going to cover are:
The process I am going to take here is focused around implementing and comparing results of each of the noise generation functions. We will compare the following qualitative values:
The quantitative values we will leverage are:
Noise can be a fairly complex topic with a large amount of information on the topic that varies from article to article. We aim to resolve a number of the difficulties with this article as well as provide a complete set of code and framework that can be reused or inspected. Noise functions do not necessarily generate those beautiful pictures you are used to seeing, it’s the layering and adding those layers together which has those various effects.
In order for us to understand these various noise functions we need to compare it to a purely random function. For this we will be leveraging each of the languages random function to generate a number between 0.0f and 1.0f. We will attempt to stick to floating points as much as the language will allows us to. This is in an effort to ensure consistency between the results. Simply put:
1: function RandomNoise(){ 2: return random(0.0f,1.0f); 3: }
1: function RandomNoise(){
2: return random(0.0f,1.0f);
3: }
We take the result of the RandomNoise Function and convert it to an RGB value between 0 and 255. For this article we will stick to shades of grey so the RGB values will all be the same.
1: int r = g = b = randomvalue * 255
The results of the RandomNoise function look as follows:
Figure 1.0 Noise Pattern
For an image 300px x 200px the average generation time sits in the 1600ms time range in JavaScript. As you can see there is no uniformity to the noise generated. The performance per language is broken down as follows:
Table 1.0 Random Noise Average Time per resolution
As you can see the performance is purely based on the number of pixels you are trying to generate as well as the language you are using.
Before we go into Perlin Noise there is a bit of understanding of the different types of interpolation that we need to have. Interpolation calculates a projected value between a set of points based on various functions or techniques.
Linear interpolation performs curve fitting using linear polynomials. It takes in 3 inputs Point1, Point2 and t which is a range between 0.0 and 1.0. The function for linear interpolation we will leverage is:
1: function lerp(v0, v1, t) 2: { 3: return v0+(v1-v0)*t; 4: }
1: function lerp(v0, v1, t)
2: {
3: return v0+(v1-v0)*t;
4: }
Cosine interpolation is a form of trigonometric interpolation which will provide us with smoother interpolations than linear. The function we will leverage is:
1: function cosinerp(v0, v1, t) 2: { 3: ft = t * 3.1415927 4: f = (1 - cos(ft)) * .5 5: return v0*(1-f) + v1*f 6: }
1: function cosinerp(v0, v1, t)
3: ft = t * 3.1415927
4: f = (1 - cos(ft)) * .5
5: return v0*(1-f) + v1*f
6: }
Cubic interpolation is a more complex form of interpolation but results in much higher amounts of smoothing between points. Its also one of the simpler options to implement although it does require 4 points and t a range which works similar to the other two versions of interpolation. CatmullRom is a variation of this with weighted multiples applied to the calculation (See source code). The implementation is as follows:
1: function cuberp(y0, y1, y2, y3, mu) 2: { 3: a0,a1,a2,a3,mu2; 4: mu2 = mu*mu; 5: a0 = y3 - y2 - y0 + y1; 6: a1 = y0 - y1 - a0; 7: a2 = y2 - y0; 8: a3 = y1; 9: return(a0*mu*mu2+a1*mu2+a2*mu+a3); 10: }
1: function cuberp(y0, y1, y2, y3, mu)
3: a0,a1,a2,a3,mu2;
4: mu2 = mu*mu;
5: a0 = y3 - y2 - y0 + y1;
6: a1 = y0 - y1 - a0;
7: a2 = y2 - y0;
8: a3 = y1;
9: return(a0*mu*mu2+a1*mu2+a2*mu+a3);
10: }
Hermite interpolation is similar to Cubic except with the addition of two parameters Tension and Bias. Tension tightens up the curvature of the points and bias twists the curve around specific points. The formula for Hermite interpolation is:
1: function hermiterp(v0, v1, v2, v3, t, tension, bias){ 2: var m0,m1,t2,t3; 3: var a0, a1, a2, a3; 4: t2 = t * t; 5: t3 = t2 * t; 6: m0 = (v1-v0)*(1+bias)*(1-tension)/2; 7: m0 += (v2-v1)*(1-bias)*(1-tension)/2; 8: m1 = (v2-v1)*(1+bias)*(1-tension)/2;| 9: m1 += (v3-v2)*(1-bias)*(1-tension)/2; 10: 11: a0 = 2*t3 - 3*t2 + 1; 12: a1 = t3 - 2*t2 + t; 13: a2 = t3 - t2; 14: a3 = -2*t3 + 3*t2; 15: 16: return(a0*y1+a1*m0+a2*m1+a3*y2); 17: }
1: function hermiterp(v0, v1, v2, v3, t, tension, bias){
2: var m0,m1,t2,t3;
3: var a0, a1, a2, a3;
4: t2 = t * t;
5: t3 = t2 * t;
6: m0 = (v1-v0)*(1+bias)*(1-tension)/2;
7: m0 += (v2-v1)*(1-bias)*(1-tension)/2;
8: m1 = (v2-v1)*(1+bias)*(1-tension)/2;|
9: m1 += (v3-v2)*(1-bias)*(1-tension)/2;
10:
11: a0 = 2*t3 - 3*t2 + 1;
12: a1 = t3 - 2*t2 + t;
13: a2 = t3 - t2;
14: a3 = -2*t3 + 3*t2;
15:
16: return(a0*y1+a1*m0+a2*m1+a3*y2);
17: }
We also need to understand some terminology before proceeding as I found that without a decent understanding of these terms the following explanations become somewhat difficult to understand first time round. These concepts are focused on how layers are added together and the various properties of the layers.
Octaves: this first term is how many layers of noise are we going to blend together. The more octaves or layers the more detail the noise has
Frequency: this is the number of segments that we are going to put into the space we are using. This is the same as frequency when it comes to sound waves or signal process. The lower the frequency the bigger the features and the higher the frequency the smaller the features.
Amplitude: this is the height of the details. The bigger the amplitude the bigger the height of the features. Generally one will decrease the amplitude for each octave to have a more general smoothing effect.
Persistence: This is the rate at which the amplitude will decrease with each octave that is added. The smaller the persistence the lower the amount of change
With these concepts we can multiply and add various layers of noise together to generate wood like textures, clouds and many other types of procedural textures.
The next one we will look at is Value noise which is one of the simplest. Value noise is a simple algorithm that leverages a grid of random values between 0 and 1 to work out what a pixel should be. Below is a picture of what Value Noise Looks like:
Figure 2.0 Value Noise
The way value noise works is by using a set of points which are randomized and then for each sample between points are interpolated between that set of randomized values. The general flow of this is as follows:
Lets have a look at each of the steps and understand them further. The first stage we generate an array of the appropriate dimension and we assign random values to this. The size of the array can vary quite widely and its worth having a look how the array size affects the out come. For a 2D noise function it will simply be:
1: for(int x = 0; x < size; x++){ 2: for(int y = 0; y < size; y++){ 3: RandomArray[x][y] = Random(); 4: } 5: }
1: for(int x = 0; x < size; x++){
2: for(int y = 0; y < size; y++){
3: RandomArray[x][y] = Random();
5: }
Now we have an array populated with random values this is our noise information that we are going to use to interpolate to have smooth transitions. Lets have a look at how this works. Given a point we map that point to the RandomArray and interpolate the value from the surrounding values points. We also need to know the position of the point given relative to the surrounding points we use this relative information to determine the interpolation value between two points. Now we interpolate and return the value for the noise. Once we populate our grid it should be something similar to this:
Figure 2.1 Populated Random Grid
Say we have a point 2,3 that we want noise for on an image that is 300x200 pixels wide we will pass in the x and y values multiplied by the inverse of the width and height of the area you wish to fill with noise. eg.
tx = 0,0033333 = 1/300 ty = 0,005 = 1/200 So we pass into the noise function 2.003333 and 3.005 (the red dot on the diagram). We now break down the point passed in into two components, its whole number and the remainder (tx and ty). With the remainder we use a smoothing function to get a new value that is used during interpolation. We then figure out which are the neighbouring points in the generated grid (the green dots on the diagram). We then interpolate the values with each other.
Figure 2.2 The Points used for interpolation to ensure smooth noise.
To view the source code for this please see the source code links in the references section. Below are the performance metrics for the different languages and resolutions.
Table 2.0 Value Noise Average Time per resolution
Perlin Noise is the award winning technique developed by Ken Perlin in 1982 and was leveraged in the motion Picture Tron. It is a gradient noise which is a pseudo random technique. Instead of storing random numbers in the grid as in Value noise and interpolating, Perlin noise stores a gradient vector. To get the values we calculate the dot products of each gradient vector. The dot product is then interpolated to get our result which looks like:
Figure 3.0 Perlin Noise
From the above image you can see Perlin noise is less uniform than Value. There are a number of great articles covering the theory behind Perlin noise which are found in the references section but I will do my best to sum up the jist of what is going on at each step. Looking at Ken Perlin’s improved Perlin noise first step is initializing the array p[]. What is array p? The P array allows us to have a predefined pseudo random allocation of number we can use to generate gradients. We could generate this array using the original method defined by Ken Perlins C code but for performance sake we use a predefined list. This p array is populated to fill 512 items.
Figure 3.1 Populating the P array
Now we have an array that can be used for random numbers we will take our input values and begin processing. It is important to note that the input expected is a floating point (eg 0.0333 or 1.0333). This is why we divide the x and y values by the width and height. This gives us a percentage which is the distance across the width or height. Using this adjusted x and y value as input we then find the cube that surrounds the inputs.
Figure 3.2 Components used for the Perlin Noise calculation
Once we have the required components we now calculate the value that we will be using for the interpolation between the various gradients. This is done using a smoothing function which given a value will return the corresponding value that is on a smoothed curve.
Figure 3.3 Smooth curve values for the Lerp
Using this information we use the values in the p[] array to get hash values that we can leverage to generate the gradients for each point. For example given that our x and y values are (0.0333, 0.0333) for x = 0 and y = 0 the hashes would look as follows:
1: A = p[0] + 0 = 151 2: AA = p[A] + 0 = p[151] = 17 3: …
1: A = p[0] + 0 = 151
2: AA = p[A] + 0 = p[151] = 17
3: …
Using those points we now calculate the various gradients for AA,AB,BA,BB and AA+1, AB+1, BA+1, BB +1.
1: x = 0.0333 2: y = 0.0333 3: AA = 17 4: h = 17 & 15 = 1 5: u = 1 < 8 ? 0.0333 : 0.0333 = 0.0333 6: v = 1 < 4 ? 1 == 12 || h == 14 ? 0.0333 : 0 = 0 7: a = h & 1 == 0 ? u : -u = 0.0333 8: b = h & 2 == 0 ? v : -v = 0; 9: 10: return a + b = 0.0333
1: x = 0.0333
2: y = 0.0333
3: AA = 17
4: h = 17 & 15 = 1
5: u = 1 < 8 ? 0.0333 : 0.0333 = 0.0333
6: v = 1 < 4 ? 1 == 12 || h == 14 ? 0.0333 : 0 = 0
7: a = h & 1 == 0 ? u : -u = 0.0333
8: b = h & 2 == 0 ? v : -v = 0;
9:
10: return a + b = 0.0333
With each of those gradients we now perform a Linear Interpolation between those using the smoothed values and that’s about it.
Table 3.0 Perlin Noise Average Time per resolution
Simplex Noise developed by Ken Perlin in 2001 has similar results to Perlin Noise with less computational requirements than Perlin. The idea of Simplex is to divide the N dimensional space into triangles that reduces the number of data points. In Perlin noise we would find the cube that the point we are given in resides and find the points related. Visually the result is not much different from Perlin noise.
Figure 4.0 Simplex Noise
With simplex we are finding the shape that is based on a equilateral triangle and returning fewer points. The major difference Basically it takes the previous grid and skews it and forms triangles for the simplex representation. The gradients for each of the points are then generated and then the rest of the implementation is pretty much the same.
[TODO – Diagram showing the difference between Perlin and Simplex]
The results are good and the performance is much better than Perlin with higher dimensions but it is quite complex to understand
Table 4.0 Simplex Noise Average Time per resolution
Worley Noise (Aka Cell Noise) was developed by Steven Worley in 1996 which is very useful in generating stone, water or cell noise textures. The way Worley noise works is that is has a random set of feature points. Given a specific point it calculates the distance of that point to the nearest feature point and uses that for the information. The result is seen below:
Figure 5.0 Worley Noise
What is needed is a set of random points which we compare against. Then we calculate the distance from to the nearest point from the point supplied. Worley’s algorithm has a more efficient implementation of this concept which performs the following steps:
Figure 5.1 How the noise value is determined
We can change our results by changing the number of feature points, the way distance is computed and the size of the grid used. If you invert the image you can see the points and the distance information clearly.
Table 5.0 Worley Noise Average Time per resolution
Table 6.0 Summarized findings
Source Code:
Sharp Noise : https://sharpnoise.codeplex.com/
References:
Bourke, P (1999) Interpolation Methods, [Online] Available: http://paulbourke.net/miscellaneous/interpolation/ [7 Feb 2014]
(2012) Noise Part 1, [Online], Available: http://scratchapixel.com/lessons/3d-advanced-lessons/noise-part-1/ [12 Feb 2014]
(2010) Value_Noise – Explanation of Value Noise, [Online], Available: http://code.google.com/p/fractalterraingeneration/wiki/Value_Noise [12 Feb 2014]
(2013) Perlin Noise, Wikipedia, [Online], Available: http://en.wikipedia.org/wiki/Perlin_noise [5 Feb 2014]
Zucker, M (2001) The Perlin noise math FAQ, [Online], Available: http://webstaff.itn.liu.se/~stegu/TNM022-2005/perlinnoiselinks/perlin-noise-math-faq.html [5 Feb 2014]
Hugo, E (2003) Perlin noise,[Online] Available: http://freespace.virgin.net/hugo.elias/models/m_perlin.htm [5 Feb 2014] Perlin, K () Noise and Turbulance, [Online] Available: http://www.mrl.nyu.edu/~perlin/doc/oscar.html#noise [24 Feb 2014] Perlin, K () Noise Hardware, [Online] Available: http://www.csee.umbc.edu/~olano/s2002c36/ch02.pdf [26 Mar 2014]
Perlin, K (2002) Improved Noise Reference Implementation,[Online], Available: http://mrl.nyu.edu/~perlin/noise/ [7 Feb 2014]
Tulleken, H (2009) How to use Perlin Noise in your games, [Online], Available: http://devmag.org.za/2009/04/25/perlin-noise/ [7 Feb 2014]
Thomas, K (2011) Perlin Noise in JavaScript, [Online] Available: http://asserttrue.blogspot.co.uk/2011/12/perlin-noise-in-javascript_31.html [26 Mar 2014]
Explanation of Perlin Noise, [Online] Available: http://code.google.com/p/fractalterraingeneration/wiki/Perlin_Noise [26 Mar 2014]
Simplex Noise, [Online] Available: http://en.wikipedia.org/wiki/Simplex_noise [26 Mar 2014]
Gustavson, S (2005) Simplex noise demystified, [Online] Available: http://webstaff.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf [26 Mar 2014]
Explanation of Simplex Noise, [Online] Available: http://code.google.com/p/fractalterraingeneration/wiki/Simplex_Noise [26 Mar 2014]
Worley Noise, [Online] Available: http://en.wikipedia.org/wiki/Worley_noise [26 Mar 2014]
FreeTheNation (alias) (2013) Cell Noise, [Online] Available: http://aftbit.com/cell-noise-2/ [26 Mar 2014]
Worley, S, A Cellular Texture Basis Function, [Online] Available: http://www.rhythmiccanvas.com/research/papers/worley.pdf [26 Mar 2014]
Endre, S (2012) Worley Noise Cellular Texturing, [Online] Available: http://esimov.com/2012/05/worley-noise-cellular-texturing#.Ux790v_MOM8 [26 Mar 2014]
Nouanesengsy, B CSE 782 Lab 4, [Online] Available: http://www.cse.ohio-state.edu/~nouanese/782/lab4/ [26 Mar 2014]
Rosen, C (2006) Cell Noise and Processing, [Online] Available: http://www.carljohanrosen.com/share/CellNoiseAndProcessing.pdf [26 Mar 2014]
Appendix A – Data Results
Table A.0.1 Javascript Random Noise in milliseconds
Table A.0.2 Javascript Value Noise in milliseconds
Table A.0.3 Javascript Perlin Noise in milliseconds
Table A.0.3 Javascript Simplex Noise in milliseconds
Table A.0.4 Javascript Worley Noise in milliseconds
C# Random Noise
C# Value Noise
C# Perlin Noise
C# Simplex Noise
C# Worley Noise
C++ Random Noise
C++ Value Noise
C++ Perlin Noise
C++ Simplex Noise
C++ Worley Noise