Search code examples
openglblockfragment-shaderperlin-noise

Perlin Noise block grid


I encountered a problem trying to compute Perlin noise using an OpenGL fragment shader. The result is blocky and not continuous at all. enter image description here

I'm trying to use this kind of implementation: enter image description here

I can't figure out the problem, here is my fragment shader code:

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

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


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


// smooth interpolation funtion
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 coloumn and row of the bottom left 
    //point of the square in wich 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);

}


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.