Perlin noise is quite similar to value noise, but instead of interpolating single hash values, it interpolates between gradients that go in random directions.
This can produce smoother, more natural-looking results, but at a higher performance cost.
Let’s go through the steps:
Step 1: Vector Hash
We need a vec2 hash instead of a single value:
return fract(sin(p * mat2(0.129898, 0.78233, 0.81314, 0.15926)) * 43758.5453);
I’m calling this function “hash2” because it returns a vec2 output. The mat2 here is a shorter way of writing:
p.x * vec2(0.129898, 0.81314) + p.y*vec2(0.78233, 0.15926)
And to get a random unit vector:
return normalize(hash2(p) - 0.5);
I’m calling this “hash2_norm”. We’ll need both functions later.
Step 2: Sample corners
Just like with value noise, we divide into cells and sample direction vectors for the 4 corners:
vec2 dir_corner00 = hash2_norm(cell+off.xx);
vec2 dir_corner10 = hash2_norm(cell+off.yx);
vec2 dir_corner01 = hash2_norm(cell+off.xy);
vec2 dir_corner11 = hash2_norm(cell+off.yy);
But calculate the gradients, we need to compute the dot product of the direction and the difference between the sample point and the corner.
The dot product here just tells how far along the random axis, the current sample point is.
float grad_corner00 = dot(dir_corner00, off.xx-sub);
float grad_corner10 = dot(dir_corner10, off.yx-sub);
float grad_corner01 = dot(dir_corner01, off.xy-sub);
float grad_corner11 = dot(dir_corner11, off.yy-sub);
Don’t worry! This is the hardest part!
Step 3: Interpolate
The same process as with the value noise, but we’ll use “quintic” interpolation instead of cubic.
vec2 quint = sub*sub*sub*(10.0 + sub*(-15.0 + 6.0*sub));
Feel free to compare to cubic and linear interpolation!
Now we interpolate:
float hor0 = mix(grad_corner00, grad_corner10, quint.x);
float hor1 = mix(grad_corner01, grad_corner11, quint.x);
Note: these values can range from -sqrt(2.0) to +sqrt(2.0), so to get a value between 0 and 1, I multiply by 0.7 (approximately sqrt(0.5)) and add 0.5.
return mix(hor0, hor1, quint.y) * 0.7 + 0.5;
And that’s it. You should get something like this: