I’ve gone about using a (from what I can tell) rather different implementation of 2D Perlin Noise described in this paper by the University of Cambridge:Appendix B – Perlin Noise But the function produces inconsistencies between rows:
Inconsistent 2D Perlin Noise
Here’s my code, just barebones JavaScript in HTML5:
<!-- I've been trying to make functional classic perlin noise for years and this is
the closest I've come to achieving that goal.
I used this paper:
https://www.cl.cam.ac.uk/teaching/1718/FGraphics/Appendix%20B%20-%20Perlin%20Noise.pdf
-->
<!DOCTYPE html>
<html>
<canvas id = "canvas" width = "512" height = "512"></canvas>
<script>
let canvas = document.getElementById("canvas");
let context = canvas.getContext("2d");
const n = 8;
const rand_uVec = function () {
const a = Math.random() * (Math.PI * 2.0);
return {x: Math.cos(a), y: Math.sin(a)};
}
const seedArray = function () {
let s = [];
for (let x = 0; x < n + 1; x++) {
s[x] = [];
for (let y = 0; y < n + 1; y++) s[x][y] = rand_uVec();
} return s;
}
const seed = seedArray();
function PerlinNoise2D(x, y) {
const s = Math.floor(x), t = Math.floor(y);
/*
* (s + 0, t + 0),
* (s + 1, t + 0),
* (s + 1, t + 1),
* (s + 0, t + 1)
*/
// define the grid cell corner coordinates
const s0 = s + 0, t0 = t + 0,
s1 = s + 1, t1 = t + 1;
// get the random gradient vectors
const v00 = seed[s0][t0],
v10 = seed[s1][t0],
v11 = seed[s1][t1],
v01 = seed[s0][t1];
// get the difference between the cell corner and sample point
const d00 = {x: x - s0, y: y - t0},
d10 = {x: x - s1, y: y - t0},
d11 = {x: x - s1, y: y - t1},
d01 = {x: x - s0, y: y - t1};
const dot = function (v0, v1) {
return v0.x * v1.x + v0.y * v1.y;
}
const UL = dot(v00, d00),
UR = dot(v10, d10),
LR = dot(v11, d11),
LL = dot(v01, d01);
const S = function (t) {
return 3 * t ** 2 - 2 * t ** 3;
}
// the problem isn't that I'm using an existing x and y variable in these two functions
const L = function (sx, sy) {
return LL + S(sx - Math.floor(sx)) * (LR - LL);
}
const U = function (sx, sy) {
return UL + S(sx - Math.floor(sx)) * (UR - UL);
}
return L(x, y) + S(y - Math.floor(y)) * (U(x, y) - L(x, y));
}
ImageData.prototype.fragColor = function (x, y, s) {
this.data[(y * this.width + x) * 4 + 0] = s;
this.data[(y * this.width + x) * 4 + 1] = s;
this.data[(y * this.width + x) * 4 + 2] = s;
this.data[(y * this.width + x) * 4 + 3] = 255;
}
let noiseTexture = context.getImageData(0, 0, canvas.width, canvas.height);
for (let x = 0; x < canvas.width; x++) {
for (let y = 0; y < canvas.height; y++)
// noiseTexture.fragColor(x, y, Math.random() * 255);
noiseTexture.fragColor(x, y, PerlinNoise2D(x * (n / canvas.width), y * (n / canvas.height)) * 255);
} context.putImageData(noiseTexture, 0, 0);
const dataURL = canvas.toDataURL('image/png');
const link = document.createElement('a');
link.href = dataURL;
link.download = 'noise.png';
document.body.appendChild(link);
link.click();
document.body.removeChild(link); // Remove the temporary link
</script>
</html>
I’ve seen problems like this before with other Perlin Noise functions on Stack Overflow, and I have tried changing around the order of some of the input coordinates, but usually the outcome is an even more chopped up noise graph, whereas I expected something with smoother results.
I would appreciate if any answers could follow the implementation of the paper I’m following, rather than the usually implementation with nested linear interpolation functions (I get that it’s the same concept, I just think mine looks more simplistic). I would also appreciate it if your answers could concern only the basest implementation of Perlin Noise without seed implementation/permutation tables. Thanks!