A good random number generator produces numbers that have no relationship and show no discernible pattern. As we are beginning to see, a little bit of randomness can be a good thing when programming organic, lifelike behaviors. However, randomness as the single guiding principle is not necessarily natural. An algorithm known as “Perlin noise,” named for its inventor Ken Perlin, takes this concept into account. Perlin developed the noise function while working on the original Tron movie in the early 1980s; it was designed to create procedural textures for computer-generated effects. In 1997 Perlin won an Academy Award in technical achievement for this work. Perlin noise can be used to generate various effects with natural qualities, such as clouds, landscapes, and patterned textures like marble.
Perlin noise has a more organic appearance because it produces a naturally ordered (“smooth”) sequence of pseudo-random numbers. The graph on the left below shows Perlin noise over time, with the x-axis representing time; note the smoothness of the curve. The graph on the right shows pure random numbers over time.
Processing has a built-in implementation of the Perlin noise algorithm: the function noise(). The noise() function takes one, two, or three arguments, as noise is computed in one, two, or three dimensions. Let’s start by looking at one-dimensional noise.
The Processing noise reference tells us that noise is calculated over several “octaves.” Calling the noiseDetail() function will change both the number of octaves and their importance relative to one another. This in turn changes how the noise function behaves.
An online lecture by Ken Perlin lets you learn more about how noise works from Perlin himself.
Consider drawing a circle in our Processing window at a random x-location.
float x = random(0, width);
ellipse(x, 100, 50, 50);
Now, instead of a random x-location, we want a Perlin noise x-location that is “smoother.” You might think that all you need to do is replace random() with noise(), i.e.
float x = noise(0,width);
While conceptually this is exactly what we want to do—calculate an x-value that ranges between 0 and the width according to Perlin noise—this is not the correct implementation. While the arguments to the random() function specify a range of values between a minimum and a maximum, noise() does not work this way. Instead, the output range is fixed—it always returns a value between 0 and 1. We’ll see in a moment that we can get around this easily with Processing’s map() function, but first we must examine what exactly noise()expects us to pass in as an argument.
We can think of one-dimensional Perlin noise as a linear sequence of values over time. For example:
Now, in order to access a particular noise value in Processing, we have to pass a specific "moment in time" to the noise() function. For example:
According to the above table, noise(3) will return 0.364 at time equals 3. We could improve this by using a variable for time and asking for a noise value continuously in draw().
float t = 3;
void draw() {
float n = noise(t);
println(n);
}
The above code results in the same value printed over and over. This happens because we are asking for the result of the noise() function at the same point in time—3—over and over. If we increment the time variable t, however, we’ll get a different result.
float t = 0;
void draw() {
float n = noise(t);
println(n);
t+= 0.01;
}
How quickly we increment t also affects the smoothness of the noise. If we make large jumps in time, then we are skipping ahead and the values will be more random.
Now, Normally we will be interested in ranges of numbers greater than the default 0 ... 1 range. We solve this by multiplying whatever number comes out of noise(). For example if we multiply noise() by 100, we will get a number between 0 and 100. ** See code below**
float my_num = 0;
void setup() {
size(400, 400);
}
void draw() {
background(255 * noise(my_num + 100));
stroke(255);
// noise() returns a number between 0 and 1
// when we multiply noise() by width, we get a number between 0 and width
float x = noise(my_num) * width;
// draw a vertical line
line(x, 0, x, height);
// we add 40 to my_num to avoid getting the exact same random number
// we got on our previous call to the noise() function
float y = noise(my_num + 40) * height;
// draw a horizontal line
line(0, y, width, y);
my_num = my_num + 0.02;
}
float my_num = 10;
void setup() {
size(400, 400);
background(#810C2F);
fill(255);
noStroke();
smooth();
rectMode(CENTER);
}
void draw() {
background(#810C2F);
// move the origin to a random position in the screen.
// the random position is calculated using the noise() function
translate(width * noise(my_num + 80), height * noise(my_num + 100));
// add some random rotation using noise()
rotate(10 * noise(my_num + 40));
// draw a rectangle with a random width and height, again using noise()
rect(0, 0, 200 * noise(30 + my_num), 200 * noise(my_num));
// increase the counter variable which is used in noise() calls.
my_num = my_num + 0.02;
}
Now lets work on LANDSCAPES
float time = 0;
void setup() {
size(400, 400);
}
void draw() {
background(255);
float x = 0;
while (x < width) {
line(x, 200 + 50 * noise(x / 100, time), x, height);
x = x + 1;
}
time = time + 0.02;
}