openglglslfragment-shaderperlin-noise

Perlin Noise block grid


Using this kind of implementation:

enter image description here

...I'm trying to compute Perlin noise using an OpenGL fragment shader:

#version 330 core
out vec3 color;
in vec4 p;
in vec2 uv;

// random value for x gradient coordinate
float randx(vec2 co){
    return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
}

// random value for y gradient coordinate
float randy(vec2 co){
    return fract(cos(dot(co.xy ,vec2(4.9898,78.233))) * 68758.5453);
}

// smooth interpolation function
float smoothInter(float x){
    return 6*x*x*x*x*x -15*x*x*x*x + 10*x*x*x;
}

float grid_dim = 10.0f;

void main() {
    // Get column and row of the bottom left 
    //point of the square in which the point is in the grid
    int col = int(uv.x * grid_dim);
    int row = int(uv.y * grid_dim);

    // Get the 4 corner coordinate of the square, 
    //divided by the grid_dim to have value between [0,1]
    vec2 bl = vec2(col, row) / 10.0f;
    vec2 br = vec2(col+1, row) / 10.0f;
    vec2 tl = vec2(col, row+1) / 10.0f;
    vec2 tr = vec2(col+1, row+1) / 10.0f;
    
    // Get vectors that goes from the corner to the point
    vec2 a = normalize(uv - bl);
    vec2 b = normalize(uv - br);
    vec2 c = normalize(uv - tl);
    vec2 d = normalize(uv - tr);
    
    // Compute the dot products
    float q = dot(vec2(randx(tl),randy(tl)), c);
    float r = dot(vec2(randx(tr),randy(tr)), d);
    float s = dot(vec2(randx(bl),randy(bl)), a);
    float t = dot(vec2(randx(br),randy(br)), b);
    
    // interpolate using mix and our smooth interpolation function
    float st = mix(s, t, smoothInter(uv.x));
    float qr = mix(q, r, smoothInter(uv.x));
    float noise = mix(st, qr, smoothInter(uv.y));
    
    // Output the color
    color = vec3(noise, noise, noise);
}

...but the result is blocky and not continuous at all:

enter image description here

Where is the problem?


Solution

  • In the last few rows, you are calling smoothInter() on the global x and y coordinates, when you need to call it on the local coordinates.

    float st = mix(s, t, smoothInter( (uv.x - col) * grid_dim ));
    float qr = mix(q, r, smoothInter( (uv.x - col) * grid_dim ));
    float noise = mix(st, qr, smoothInter( (uv.y - row) * grid_dim ));
    

    Multiplying by grid_dim here because your grid cells are not unit width. smoothInter() should take values between 0 and 1 and this transform ensures that.

    I also had to remove the normalize() calls, and instead "normalise" the result into the range [0,1]. This was tricky, I assume because of your method of generating the random gradient vectors at the grid vertices. As it stands, your code seems to output values between -2500 and +2500 approximately. Once I scaled this to the right range I had some undesirable regularity appearing. I again put this down to the choice of prng.