0

My problem that my perlin noise is repeating itself very obviously in very small spaces. Here is an image of what it going on. I know that this does happen after a certain point with all perlin noise, but it seems to be happening almost immediately with mine. I believe that it is caused by my really awful pseudorandom gradient generator, but Im not sure. My code is below.

enter image description here

As a side note, my perlin noise seems to generate very small values, between -.2 and positive .2 and I think this is also caused by my pseudorandom gradient generator.

If anyone has any advice on improving this part of my code, please feel free to tell me. Any ideas would be helpful right now.

Thanks to everyone in advance!

public class Perlin {
    int[] p = new int[255];
    public Perlin() {
        for(int i = 0; i < p.length; i++)
            p[i] = i;

        shuffle(p);
    }

    int grads[][] = {
            {1,0},{0,1},{-1,0},{0,-1},
            {1,1},{-1,1},{1,-1},{-1,-1}
    };

    public double perlin (double x, double y) {

        int unitX = (int)Math.floor(x) & 255; // decide unit square
        int unitY = (int)Math.floor(y) & 255; // decide unit square

        double relX = x-Math.floor(x); // relative x position
        double relY = y-Math.floor(y); // relative y position

        // bad pseudorandom gradient -- what i think is causing the problems
        int units = unitX+unitY;
        int[] gradTL = grads[p[(units)]%(grads.length)];
        int[] gradTR = grads[p[(units+1)]%(grads.length)];
        int[] gradBL = grads[p[(units+1)]%(grads.length)];
        int[] gradBR = grads[p[(units+2)]%(grads.length)];

        // distance from edges to point, relative x and y inside the unit square
        double[] vecTL = {relX,relY};
        double[] vecTR = {relX-1,relY};
        double[] vecBL = {relX,relY-1};
        double[] vecBR = {relX-1,relY-1};

        // dot product
        double tl = dot(gradTL,vecTL);
        double tr = dot(gradTR,vecTR);
        double bl = dot(gradBL,vecBL);
        double br = dot(gradBR,vecBR);

        // perlins fade curve
        double u = fade(relX);
        double v = fade(relY);

        // lerping the faded values
        double x1 = lerp(tl,tr,u);
        double y1 = lerp(bl,br,u);

        // ditto
        return lerp(x1,y1,v);
    }
    public double dot(int[] grad, double[] dist) {
        return (grad[0]*dist[0]) + (grad[1]*dist[1]);
    }
    public double lerp(double start, double end, double rate){
        return start+rate*(end-start);
    }
    public double fade(double t) {
        return t*t*t*(t*(t*6-15)+10);
    }
    public void shuffle(int[] p) {
        Random r = new Random();
        for(int i = 0; i < p.length; i++) {
            int n = r.nextInt(p.length - i);
            // do swap thing
            int place = p[i];
            p[i] = p[i+n];
            p[i+n] = place;
        }
    }
}

A side note on my gradient generator, I know Ken Perlin used 255 because he was using bits, I just randomly picked it. I dont think it has any effect on the patterns if it is changed.

Reblochon Masque
  • 35,405
  • 10
  • 55
  • 80
Sprockget
  • 1
  • 4
  • 2
    "Perlin used 255 because he was using bits, I just randomly picked it"... You should probably first implement the original algorithm, then modify it. – Reblochon Masque Mar 30 '18 at 00:13
  • I have a very bare bones version of the algorithm, it is not the most efficient and not the most complicated, but it is still the original algorithm. – Sprockget Mar 30 '18 at 02:24
  • "I dont think it has any effect on the patterns if it is changed" - then you are guessing. It's easy enough to verify & you should do so. Also, you have an option to pass a seed into the constructor to initialize your RNG. If you don't you'll never be able to recreate specific problem cases nor be able to determine if you've actually fixed them. – Pikalek Mar 30 '18 at 16:25
  • I checked and switched the length of p[] around and nothing changed. Also, this is not a specific problem case, patterns such as this one appear every time I run the program. – Sprockget Mar 31 '18 at 14:28

1 Answers1

0

Your intuition is correct. You calculate:

int units = unitX+unitY;

and then use that as the base of all your gradient table lookups. This guarantees that you get the same values along lines with slope -1, which is exactly what we see assuming (0, 0) is the upper-left corner.

I would suggest using a real hash function to combine your coordinates: xxHash, Murmur3, or even things like CRC32 (which isn't meant to be a hash) would be much better than what you're doing. You could also implement Perlin's original hash function, although it has known issues with anisotropy.

D0SBoots
  • 705
  • 6
  • 18