22
$\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$

1 Answer 1

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$
13
  • $\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, 2016 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$ May 15, 2016 at 19:15
  • 1
    $\begingroup$ @cat I doubt GLSL has a PRNG, given that GLSL programs are deterministic. $\endgroup$
    – user253751
    May 15, 2016 at 22:20
  • 1
    $\begingroup$ Looks like there are several potential new questions in this comment thread... $\endgroup$ May 16, 2016 at 0:22
  • 1
    $\begingroup$ I was having those artifacts and this rand() function fixed it. Problem is after I walked like 2km in my terrain, artifacts like the OPs started to show up again. It was using the hash function here: amindforeverprogramming.blogspot.com/2013/07/… that caused the artifacts to go away (Except at distances of 100km, bc of imprecision, but thats okay I just had to split into chunks and got that to work by hashing both values, which will let the perlin noise run nearly indefinitely). So, I'll leave this here to maybe help anyone who has the same issue. $\endgroup$ Oct 27, 2019 at 20:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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