This month we looked at
creative uses for not quite random noise, including a brief insight into how Perlin noise works.
Noise is an important algorithm for digital artists, 3d designers and is used widely in visual effects.
This month there was no video recording of the talk, so this blog will be a little more narrative than usual.
The slides are at
https://goo.gl/eYgqq6 and contain links to code and further references.
Motivation - When Random Isn't Useful
We started by looking at a very simple landscape.
We asked ourselves how we could generate such a landscape using our computers. An initial suggestion is to use random numbers for the height (altitude) of each part of the terrain. That's not an entirely bad suggestion.
We looked at an example of heights created by purely random numbers. The results didn't look very realistic.
We discussed what would need to be improved to make the terrain more like a natural landscape. The main idea was that the jump between successive values was too high, and a more realistic landscape would moderate the changes. In other words, the jump up or down between consecutive values wouldn't be too large.
This is a problem that something called
noise solves. It is random, but no so random, because the values vary more smoothly. Noise values are not independent of the values before and after them, unlike purely random numbers which are totally independent of each other.
The results were much more realistic.
You can see the very simple
p5js code here:
https://www.openprocessing.org/sketch/501546
Why Noise Needs a Parameter
Th audience was asked to pick a random number. That was easy enough. The audience was then asked to pick a noise value. That wasn't so easy. The reason is important.
To pick a noise value, we or. the computer, needs to know were we are in relation to neighbouring noise values. Without this context, we have no way of making sure the number we pick is not too different from the neighbouring ones.
One way to know this context is to use a parameter to describe how far long the sequence of noise values we are.
And that's why the
noise() function provided by your favourite library takes a parameter. Noise values for similar parameters will be similar.
To encourage us to think about this, we considered what the noise parameter was that created these rings which have had noise added to them.
An initial thought might be that the radius of these circles has noise added using the radius as a parameter. This isn't correct, because the radius is the same all the way around the circles. What various around the circles? The angle. So the radius that is drawn is the basic circle radius plus
noise(angle).
Two Dimensional Noise
Before thinking about two-dimensional noise, we looked at 2-dimensional randomness. The following shows a 2-dimensional array filled with random numbers, represented by colours from a grey scale.
As expected, the image is random, with no intentional pattern.
In contrast, 2-dimensional noise does have a discernible structure. The following is the same grid but with noise values deciding the colour of each pixel. The blue to white scale makes the image look like a photo of clouds.
Just as before, the noise values don't vary wildly, but change in a more moderated way as we follow them up or across the grid. It's easy to see that a 2-dimensional noise function takes 2 parameters so that it knows how far along and up the sequence of noise values it is.
The code for these clouds is at
https://www.openprocessing.org/sketch/502021.
3D Landscape
Thinking back to the green landscape we started with, why don't we try again to recreate it more realistically. We don't actually need to implement a proper 3-dimensional model with perspective and so on. We can just have layers of 2-dimensional "fences" which have their height set using 2-dimensional noise.
We can start with a grid of points, which will be the points which will be raised (or lowered) according to the noise values at that position.
The results are impressive, given the simplicity of the idea.
That really does look like some kind of alien terrain, perhaps a scan of a planet's surface before landing...
Multiple Octaves of Noise
By stretching or squishing the parameters so they change slow for faster, we can control how lumpy or smooth the surface is. We saw this earlier in the talk with a simple 1-dimensional noise too.
It is common practise to combine different "octaves" of noise to have a result that has both a macro structure as well as finger-grained micro-structural detail.
You might be familiar with this idea from music or electronic signal processing.
Here's a comparison of a smooth landscape with one that has had finer-grained higher-frequency noise added to it.
Refining the Landscape
That landscape is simple and very effective. But we can do more with it.
Here is the landscape generated with 800 points. To avoid over saturation, each dot has been drawn with a high translucency. This allows detail to re-emerge even with large numbers of points being drawn - a broadly applicable technique.
The code for this sketch is at
https://www.openprocessing.org/sketch/504970.
We can use the noise values, not just to determine the landscapes height, but also the colour. Here is an example showing more Earth-like greens and blues.
We can even cut-off the lower values to create what looks like a lake or a sea.
After seeing these examples, it's not a surprise that noise is used to generate landscapes in games and films.
Simple Textures
We looked at how noise can be used to generate textures - in fact, a common use for noise. Here is the simple starting point showing rings created by sin waves using the distance from the centre of the canvas as a parameter.
That's fairly regular as we'd expect. We can adjust the colour that's drawn using 2-dimensional noise that takes the (x,y) position as parameters. The result looks remarkably like wood or a sliced rock.
It doesn't take much to refine this further to make it look like polished wood - a slight change of colour palette and a vertical squish.
The code for this wood effect is at
https://www.openprocessing.org/sketch/577875.
We also looked at an
agate texture which is just the above but with a green-blue colour scale. We evens saw the use of noise to create a lumpy fog effect to be used in
ray-tracing a 3-d scene.
Noise to create texture is a very popular technique, used directly by coders or indirectly by using modelling tools which provide textures based on noise.
Genuinely 3-Dimensional Noise
So ar we've only used 1-dimensional and 2-dimensinal noise, even when we're creating 3-dimensional scenes. The height values for the landscape are based on noise(x,y) for example.
It is possible to have 3-dimensional noise, based on 3 parameters.
The results can be used to create 3-dimensional structures. Some games use noise to create worlds which include caves.
Animated Noise
An interesting idea is to take 2-d slices of 3-d noise, and use that 3-rd parameter as time, or frame count. This way we can animate the 2-d slices.
We expect the resulting patterns to be smoothly changing because noise should vary smoothly along all its dimensions, inlacing any we consider time.
Here is an example of moving lava created by taking 2-d slices of 3-d noise and using a threshold to decide the colour.
That's rather effective! The simple code for this lava is at
https://www.openprocessing.org/sketch/577863.
How Perlin Noise Actually Works
Many guides sadly don't give any insight into how this really useful not-so-random noise is actually created.
I wrote a blog explaining it on another blog
http://makeyourownalgorithmicart.blogspot.com/2018/02/randomness-and-perlin-noise.html
Briefly, we discussed how even interpolating between random values wasn't good enough. That's because we have sharp bends (discontinuities) which we don't want for our smoother noise.
Even applying a smoothing function to this linear interpolation still has a problem.
The problem is that values that come from this process could remain high for too long a sequence, or alternatively, they could flip positive and negative too often. That is, the frequency is too free. Yo may be familiar with the term bandwidth-limited, which says that the frequency with which a signal is allowed to fluctuate is limited to a range. We want our noise to be band-width limited.
So a different approach is used. We start with a grid of regularly spaced points, but this time we place random unit vectors at each point. Their directions are totally random, and that's ok.
We then take a candidate point at
x, and calculate the dot product of the vector from the previous grid point to
x and the random unit vector at that grid point. Dot products, when normalised, only vary between -1 and +1 like a cosine wave. That gives us a nice smoothness (very smooth in fact because the first and second and derivatives of this value are also smooth). The dot product of the next unit vector and the vector from the next grid point to x is also calculated and the two are combined, even a simple addition is fine.
As
x moves along the axis, the values that result from this process are smooth, based on an underlying pure randomness, and importantly are limited in frequency. You can see that between any two grid points, the values can only flip once or never. That gives us a tight bound on frequency.
You can find a Python notebook demonstrating this idea here:
Real libraries providing noise will combine several octaves of signals generated like this. And the extension to more than 2 dimensions still follows this basic idea.
OpenSimplex Noise
Ken Perlin, who invented the Perlin noise algorithm, updated it a few years later to address some weaknesses. In higher dimensions, the original algorithm started to display patterns that were too regular and not the desired lumpy noise.
Have a look at the following comparison. The top row is too "straight" in parts.
His updated algorithm was patented, so sadly is not open source friendly. Luckily, the open source community developed an open algorithm called
OpenSimplex which you can find out more here:
I had previously used OpenSimplex to create 3-d textures for ray traced objects. This works better than mapping a 2-d pattern into 3-d spheres, which suffers from the well-known map projection distortion issues.
Moving Particles
Finally we looked at a rather different use of noise.
Following the path of one, or many, particles which move in a random direction at each step is a well known experiment. The results of can look interesting if we use the translucency technique to avoid over-saturation.
What if we changed the way these particles moved, so that they took a step in a direction determined by noise and not pure randomness?
The results are spectacular!
The image really looks like a pen and ink work. Amazing, given the simplicity of the underlying idea.
You can explore the code yourself at
https://www.openprocessing.org/sketch/578607.
As a final extra idea, we can use an image like the following, and move particles based on the underlying colour/luminosity.
We can use the luminosity to determine the colour and the length of the stroke drawn at a point. the results is rather hairy!
You can explore the code here
https://www.openprocessing.org/sketch/533299.
Feedback and Examples
Peter developed a couple of really nice animated sketches during the class using noise - take a look, they're really effective.
I was a little unsure if the audience would be bored by a talk which took a beginner-friendly approach and kept things fairly simple. I was pleased that quite a few people responded positively and - best of all - said they'd go and try using noise themselves!