21
$\begingroup$

I tried to implement Perlin Noise by myself using just the theory (following flafla2.github.io/2014/08/09/perlinnoise.html). Unfortunately I was unable to achieve the look of the "original" Perlin Noise.

What's the reason the code below renders a blocky version of Perlin Noise?

What should I improve/change in the code so that it renders Perlin Noise without the artifacts?

I suspect there might be problem either in the way I interpolate or in the grads vector. The grads vector contains dot products of (random vector for lattice point) and (the size vector) – for all 4 nearby lattice points. (The random and size vectors are described in the very first link.)

GLSL Sandbox: http://glslsandbox.com/e#32663.0

Artifacts in the noise

float fade(float t) { return t * t * t * (t * (t * 6. - 15.) + 10.); }
vec2 smooth(vec2 x) { return vec2(fade(x.x), fade(x.y)); }

vec2 hash(vec2 co) {
    return fract (vec2(.5654654, -.65465) * dot (vec2(.654, 57.4), co));
}

float perlinNoise(vec2 uv) {
    vec2 PT  = floor(uv);
    vec2 pt  = fract(uv);
    vec2 mmpt= smooth(pt);

    vec4 grads = vec4(
        dot(hash(PT + vec2(.0, 1.)), pt-vec2(.0, 1.)),   dot(hash(PT + vec2(1., 1.)), pt-vec2(1., 1.)),
        dot(hash(PT + vec2(.0, .0)), pt-vec2(.0, .0)),   dot(hash(PT + vec2(1., .0)), pt-vec2(1., 0.))
    );

    return 5.*mix (mix (grads.z, grads.w, mmpt.x), mix (grads.x, grads.y, mmpt.x), mmpt.y);
}

float fbm(vec2 uv) {
    float finalNoise = 0.;
    finalNoise += .50000*perlinNoise(2.*uv);
    finalNoise += .25000*perlinNoise(4.*uv);
    finalNoise += .12500*perlinNoise(8.*uv);
    finalNoise += .06250*perlinNoise(16.*uv);
    finalNoise += .03125*perlinNoise(32.*uv);

    return finalNoise;
}

void main() {
    vec2 position = gl_FragCoord.xy / resolution.y;
    gl_FragColor = vec4( vec3( fbm(3.*position) ), 1.0 );
}
$\endgroup$
24
$\begingroup$

The interpolation looks fine. The main problem here is that the hash function you're using isn't very good. If I look at just one octave, and visualize the hash result by outputting hash(PT).x, I get something like this:

bad hash function

This is supposed to be completely random per grid square, but you can see that it has a lot of diagonal line patterns in it (it almost looks like a checkerboard), so it's not a very random hash, and those patterns will show up in the noise produced by it.

The other problem is that your hash only returns gradient vectors in [0, 1], while they should be in [−1, 1] to get gradients in all directions. That part's easy to fix by remapping.

To fix those problems, I switched the code to use this hash function (which I learned from Mikkel Gjoel, and is probably due to a paper by W.J.J. Rey):

vec2 hash(vec2 co) {
    float m = dot(co, vec2(12.9898, 78.233));
    return fract(vec2(sin(m),cos(m))* 43758.5453) * 2. - 1.;
}

Note that due to the trig functions it's going to be a bit more expensive than your version. However, it considerably improves the appearance of the resulting noise:

fbm noise with better hash function

$\endgroup$
  • $\begingroup$ Thank you very much for your explanation. This is maybe off-topic, but I'll ask anyway; in some source codes that compute noise, people use vector vec3(1, 57, 113) to compute dot product with current coordinate (I suppose the aim is also to obtain a hash). Why this particular choice of constants (57 is approx. 1 radian in degrees, 133 = approx. 2*radian in degrees)? Is it because of periodicity in trig functions? I'm unable to google this. $\endgroup$ – sarasvati May 15 '16 at 18:55
  • 3
    $\begingroup$ @sarasvati I'm not really sure, but a guess is that 57 and 113 are chosen because they're prime-ish numbers. (113 is prime; 57 isn't, but it's 3*19, so still kinda primey...if that's a thing.) Multiplying or modding by a prime-ish number tends to jumble up the bits, so it's not an uncommon ingredient in hashes. $\endgroup$ – Nathan Reed May 15 '16 at 19:15
  • $\begingroup$ Wow, I didn't think about it this way. Thanks. $\endgroup$ – sarasvati May 15 '16 at 19:20
  • 1
    $\begingroup$ @cat I doubt GLSL has a PRNG, given that GLSL programs are deterministic. $\endgroup$ – immibis May 15 '16 at 22:20
  • 1
    $\begingroup$ Looks like there are several potential new questions in this comment thread... $\endgroup$ – trichoplax May 16 '16 at 0:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.