Making Noise Predictably
Meeting Ken Perlin
What graphs will we see today?
We will understand the basics of procedural noise generation: generating random noise-like numbers that allow us to model and create very realistic-looking textures, such as wood, terrain, mountains, and clouds.
Inspiration
What is Perlin Noise?
Ok, this is going to be a long explanation!!!
A. Inner Product Computation
Let us start by dividing up 2D space ( for now!!) into square-shaped cells. At each vertex we randomly place a unit gradient vector labelled \(r_{i}\) that points in a random direction. See the figure below:
We wish to calculate the Perlin Noise amount at any point of interest inside the cell.
- We draw difference vectors to the point from each of the 4 vertices.
- We compute the vector dot product with each of the \(r_{i}\) and the above difference vectors. ( 4 dot products )
- These are shown in the text print at the side of the figure.
Note how the 4 dot products change as you move the mouse/touchpad. This changes the 4 gradient vectors and hence the scalar dot products change in amplitude and polarity.
In a typical Perlin Noise implementation, the gradient vectors are fixed after an initial setup. So each gradient vector generates a range of dot-product values as the point of interest moves within the cell.
B. Interpolation of Dot Product values
With the 4 scalar dot products, we are now ready to compute the Perlin Noise value at the point of interest. There are several ways of doing this:
- Simply take the average
- Take a weighted average, with fixed weights.
- Use a wieghting/interpolating function: The closer a point of interest is to one or other of the cell vertices, the higher is the contribution of the corresponding dot-product.
The third approach is the one embedded within (all?) Perlin Noise implementations. The interpolating function is: \[ f(t) = 6t^5-15t^4+10t^3 \] and looks like this:
Both \(\frac{df(t)}{dt}\) and \(\frac{d^2f(t)}{dt}\) are continuous at the ends of the range of the function (t = 0 and t = 1).
\[ \begin{array}{lcl}f'(t) & = & \ \frac{d}{dt}[6*t^5 - 15*t^4 + 10*t^3]\\ & = & 30 * (t^4 - 2 * t^3 + t^2)\\ & = & 0 \ \text{@ t = 0 and t = 1} \end{array} \]
\[ \begin{array}{lcl}f''(t) & = & \ \frac{d^2}{dt^2}[6*t^5 - 15*t^4 + 10*t^3]\\ & = &60 * (2 * t^3 - 3 * t^2 + t)\\ & = & 0 \ \text{@ t = 0 and t = 1} \end{array} \] This ensures that there are not sudden changes in the noise function near about the vertices.
D. Fractal Overlay and Combining
Now that we have one grid full of a layer of noise generated by weighted dot-products, we can appreciate one more thing: we can overlay the space with several layers of such noise values. Why would this be a good idea?
This multiple layer overlay creates a very natural-looking fractal-ness in the resulting noise function. Most natural looking shapes like landscapes, mountains, vegetables, flames…all have this self-similar structure where when one zooms in, the magnified function looks pretty much like the un-zoomed version!!
So how we create and merge overlays? We create several more-closely-spaced grids, and generate noise in the same way. These layers of noise-s are scaled by a factor (Usually \(\frac{1}{2^n}\)), where \(n\) is the “order”of the layer. Each new finely-spaced layer generates similar-looking noise functions, which are combined with smaller and smaller weights to achieve that final polished fractal look of Perlin Noise.
We will explore this fractality with code.
Creating Textures and Waveforms with Perlin Noise
Wait, But Why?
References
R Package Citations
Package | Version | Citation |
---|---|---|
ambient | 1.0.2 | @ambient |
mosaicCalc | 0.6.4 | @mosaicCalc |
plot3D | 1.4.1 | @plot3D |