From above walkers, in case we want to simulate motion phenomena such that it is smooth - smooth being in terms of visual appearance - movement is not jerky / jagged - curvy / continuous.
We could constrain our normal walkers parameters further to do the trick - but end of the day
It might be a better idea to create a algorithm that produces random values that could be somewhat close to each other - when the input is also somewhat close to each other - by design choice - exactly what Perlin noise (henceforth referred to as PNoise) does [Perlin 02]
This has much more uses beyond smooth motion - any kind of animation requiring smoothness - a key part of natural looking phenomena
PNoise (Perlin Noise) definition
Such algs are called procedural noise functions - this is what we refer to when we say noise - other examples are Simplex noise, Value noise [ LSRD*10 ]
Perlin noise is a type of lattice gradient noise
Perlin noise in 2D specifically uses:
Random arrangement of the vectors:
The choice of the particular
But an important characteristic is that for a given seed, every lattice point will also have the same vector assigned to it
Noise value at any
module.perlin2 = function(x, y) {
// Find unit grid cell containing point
let X = Math.floor(x), Y = Math.floor(y);
// Get relative xy coordinates of point within that cell
x = x - X; y = y - Y;
// Wrap the integer cells at 255 (smaller integer period can be introduced here)
X = X & 255; Y = Y & 255;
// Calculate noise contributions from each of the four corners
let n00 = gradP[X+perm[Y]].dot2(x, y);
let n01 = gradP[X+perm[Y+1]].dot2(x, y-1);
let n10 = gradP[X+1+perm[Y]].dot2(x-1, y);
let n11 = gradP[X+1+perm[Y+1]].dot2(x-1, y-1);
// Compute the fade curve value for x
let u = fade(x);
// Interpolate the four results
return lerp(
lerp(n00, n10, u),
lerp(n01, n11, u),
fade(y));
};
This code can be viewed at the GitHub repo for this project here. This contains the defintion of all the other components - such as linear interpolation, ease function,
Explaining some implementation choices:
Note, what we are doing is defining noise within the
Rand_func:
noise(val) - produces 1D perlin Noise value4
Applied parameters:
Location - directly through x, y coordinates of position
range:
Direction, directly through
range =
Velocity (both magnitude and direction)
range:
/* Code for a 2D RW that is manupilated according to perlin noise.
Walk is performed by picking a noise value separately for the x-coord and y-coord
In this program noise value will be referred as n-value
*/
class Walker {
// Setting class fields - offests for each co-ordinate
// i.e. (starting point in time for the noise function)
// x and y offests are always increased and mag offest only when update() affecting_parameter is 'step'
xOffset = 0;
yOffset = 1000; // Note that the difference is arbitrary, but it is important,
magOffset = 2000; // in order to avoid co-orrelation between each direction (in a given time t)
constructor(x,y,r) {
this.x = x;
this.y = y;
this.r = r;
this.D = this.r * 2;
}
display() {
stroke(0);
fill(100);
circle(this.x, this.y,this.D);
}
update(affecting_params='direction') {
//Switch statement to determine the which parameter is being affected. Default: direction
//All of them are affected by Perlin noise
switch(affecting_params){
// Changing the direction in which the walker is moving
case 'direction':
this.x += map(noise(this.xoff),0,1,-1,1)*4; //map() scales n-value to (-1,1) from (0,1) - Enabling RW to move in all directions
this.y += map(noise(this.yoff),0,1,-1,1)*4; //4 is the maginitude of the step. An arbitrary standard accross all RW methods
break;
// Directly changing the location of the walker
case 'location':
this.x = noise(this.xoff)*width; //noise(t)*k - scales n-value to (0,k) from (0,1)
this.y = noise(this.yoff)*height; //Therefore here x-values are (0,width) and y-values are (0,height)
break;
// Changing both step size and direction
case 'step':
let vel_mag = noise(this.zoff)*(this.r*1.5);
this.x += map(noise(this.xoff),0,1,-1,1)*vel_mag;
this.y += map(noise(this.yoff),0,1,-1,1)*vel_mag;
this.zoff += 0.01;
break;
}
this.xoff += 0.01;
this.yoff += 0.01;
}
let w;
function setup() {
createCanvas(windowWidth, windowHeight);
background(255);
w = new Walker(width/2, height/2, 8);
}
function draw() {
// Be aware of which function you call first according to the step type you chose
w.display();
w.update('direction');
}
Note that the implementation code for the simulation is syntatically different from the above code, but the ideas are the same. Main difference is that the order of calling display() and update() is taken care of by the program, and instead of a switch statement we use an object (with a function for each param).
A simulation of a similar implementation of the Perlin Noise walker is added to the 4th chapter, in section 4.3.4.
This is in the improved Perlin noise - made in 2002, the original noise implementation (introduced 1984) has a set of 256 randomly generated vectors - and there are other changes that were made to the implementation. Perlin further made even more improvements to the implementation, by changing the definition grid structure and introduced a new algorithm - Simplex noise.
Because the idea of the algorithm is more important than any specific implementation for the purposes of this thesis, these differences are omitted from discussion
An advantage of this choice is - neighbouring grids will not end up being too similar - which would cause a large area of very similar values.
Note that this is a native JS implementation hence not written using p5 functions. And the implemented function for Perlin noise in p5.js called perlin() is a different noise algorithm (value noise) which is what we use in 3.3.2 for our Perlin Walker. But this doesn’t the output much, as implementations of both noises in 1D are similar.
1D PNoise works analogous to how 2D PNoise was explained above - in fact p5.js implementation and actual implementation of 1D PNoise works the same way.