Search code examples
c#noise

2D Perlin Noise


I have fully mastered the art of Perlin Noise in 3D, and now I'm trying to use my same implementation for a 2D algorithm. The problem seems to be in picking my gradient directions. In 3D I use 16 gradients in evenly distributed directions and this works great. In 2D I figured I'd use 8 gradients. up, down, left, right, and the four diagonal directions.

Here is what I get:

enter image description here

The general look of the noise is always correct, but the edges of the squares don't quite match up. I have also tried using other gradients or fewer gradients but get similar results. Here in another example you can see that the edges do match up sometimes and the results are fine in that area -

enter image description here

When I don't use gradients and instead just interpolate between a value picked randomly at each of the 4 corners I get the right results, which is what makes me think it is the gradient part that is messing it up.

Here is my code:

//8 different gradient directions
private Point[] grads = new Point[] { 
    new Point(0, 1), new Point(1, 1), new Point(1, 0), new Point(1, -1), 
    new Point(0, -1), new Point(-1, -1), new Point(-1, 0), new Point(-1, 1),};

//takes the dot product of a gradient and (x, y)
private float dot2D(int i, float x, float y)
{
    return
        grads[i].X * x + grads[i].Y * y;
}

public float Noise2D(float x, float y)
{
    int
        ix = (int)(x),
        iy = (int)(y);

        x  = x - ix;
        y  = y - iy;

    float
        fx  = fade(x),
        fy  = fade(y);

        ix &= 255;
        iy &= 255;

    // here is where i get the index to look up in the list of 
    // different gradients.
    // hashTable is my array of 0-255 in random order
    int
        g00 = hashTable[ix +     hashTable[iy    ]],
        g10 = hashTable[ix + 1 + hashTable[iy    ]],
        g01 = hashTable[ix +     hashTable[iy + 1]],
        g11 = hashTable[ix + 1 + hashTable[iy + 1]];

    // this takes the dot product to find the values to interpolate between
    float
        n00 = dot2D(g00 & 7, x, y),
        n10 = dot2D(g10 & 7, x, y),
        n01 = dot2D(g01 & 7, x, y),
        n11 = dot2D(g11 & 7, x, y);

    // lerp() is just normal linear interpolation
    float
        y1 = lerp(fx, n00, n10),
        y2 = lerp(fx, n01, n11);
    return
        lerp(fy, y1, y2);
}

Solution

  • I had to change this:

                n00 = dot2D(g00 & 7, x, y),
                n10 = dot2D(g10 & 7, x, y),
                n01 = dot2D(g01 & 7, x, y),
                n11 = dot2D(g11 & 7, x, y);
    

    to this:

                n00 = dot2D(g00 & 7, x    , y    ),
                n10 = dot2D(g10 & 7, x - 1, y    ),
                n01 = dot2D(g01 & 7, x    , y - 1),
                n11 = dot2D(g11 & 7, x - 1, y - 1);
    

    Basically just subtracting 1 from the x and y where needed.