I'm trying to implement Perlin Noise. Earlier I implemented it using HTML Canvas and then converted it to WebGL2 since I wanted a faster implementation. But the WebGL2 code has obvious edge artifacts in the generated noise where the noise function is not smooth.
I used Understanding Perlin Noise as my reference for understanding Perlin Noise. However, I did use a different implementation for calculating the dot product of the gradient and distance vectors since I could not get the default implementation to work.
This is the resulting noise that I'm getting:
This is the noise I was getting in my Canvas implementation:
My algorithm for both the Canvas and WebGL is the same. I have attached my WebGL code below. Any ideas why this is happening?
let gl = canvas.getContext("webgl2");
if (!gl) {
console.log("WebGL2 support not found");
}
let octaveCount = 6;
let persistence = 1.0;
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
const vertexSrc = `
#version 300 es
precision mediump float;
vec2 quad[6] = vec2[](
vec2(-1.0, -1.0),
vec2(-1.0, 1.0),
vec2(1.0, -1.0),
vec2(-1.0, 1.0),
vec2(1.0, -1.0),
vec2(1.0, 1.0)
);
void main() {
gl_Position = vec4(quad[gl_VertexID], 0.0, 1.0);
}
`;
const fragmentSrc = `
#version 300 es
precision mediump float;
uniform vec2 offset;
uniform int octaveCount;
uniform float persistence;
uniform vec2 resolution;
out vec4 color;
int p[256] = int[](151,.../*abbreviated for conveinence*/..180);
vec2 grad[256] = vec2[](
vec2(1, 0),.../*abbreviated for conveinence*/...vec2(0.999698818696, -0.0245412285229)
);
float fade(float t) {
return t * t * t * (t * (t * 6.0 - 15.0) + 10.0);
}
vec2 getVec(int xi, int yi) {
int a = p[xi & 255];
int b = p[yi & 255];
int val = p[(a + b) & 255];
return grad[val];
}
float perlinAux(vec2 st) {
int xi = int(round(st.x)) & 255;
int yi = int(round(st.y)) & 255;
float xf = st.x - float(xi);
float yf = st.y - float(yi);
float u = fade(xf);
float v = fade(yf);
float n00 = dot(vec2(xf, yf), getVec(xi, yi));
float n01 = dot(vec2(xf, yf - 1.0), getVec(xi, yi + 1));
float n10 = dot(vec2(xf - 1.0, yf), getVec(xi + 1, yi));
float n11 = dot(vec2(xf - 1.0, yf - 1.0), getVec(xi + 1, yi + 1));
float x1 = mix(n00, n10, u);
float x2 = mix(n01, n11, u);
return (mix(x1, x2, v) + 1.0) / 2.0;
}
float perlin(vec2 offset, vec2 st, int octaveCount, float persistence) {
float total = 0.0;
float freq = 1.0;
float amp = 1.0;
float maxValue = 0.0;
for (int i = 0; i < octaveCount; i++) {
total += perlinAux(offset + st * freq) * amp;
maxValue += amp;
amp *= persistence;
freq *= 2.0;
}
return total / maxValue;
}
void main() {
vec2 st = gl_FragCoord.xy / resolution;
float perlinVal = perlin(offset, st, octaveCount, persistence);
color = vec4(perlinVal, perlinVal, perlinVal, 1.0);
}
`;
function compileShader(shaderSource, shaderType) {
const shader = gl.createShader(shaderType);
gl.shaderSource(shader, shaderSource);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
throw "Shader compile failed with: " + gl.getShaderInfoLog(shader);
}
return shader;
}
const vertexShader = compileShader(vertexSrc.trim(), gl.VERTEX_SHADER);
const fragmentShader = compileShader(fragmentSrc.trim(), gl.FRAGMENT_SHADER);
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
throw "Program link failed with: " + gl.getProgramInfoLog(program);
}
const offsetLOC = gl.getUniformLocation(program, "offset");
const resolutionLOC = gl.getUniformLocation(program, "resolution");
const octaveCountLOC = gl.getUniformLocation(program, "octaveCount");
const persistenceLOC = gl.getUniformLocation(program, "persistence");
gl.useProgram(program);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.clearColor(0, 0, 0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.uniform2f(offsetLOC, 0.0, 0.0);
gl.uniform2f(resolutionLOC, gl.canvas.width, gl.canvas.height);
gl.uniform1i(octaveCountLOC, octaveCount);
gl.uniform1f(persistenceLOC, persistence);
gl.drawArrays(gl.TRIANGLES, 0, 6);