Search code examples
javanoise

Strange result from perlin noise generator


I have been experimenting with perlin noie generation for a game i want to program but i have run into some strange results. When i let my code run and draw the result in grayscale i get the following image:

enter image description here

I based my code on the wikipedia page on perlin noise and on one of the reference pages (links below). It is supposd to generate perlin noise point per point based on a pregenerated grid of semi-random vectors.

public static double[][] generateMap(long seed, int width, int height) {
    double[][] map = new double[width][height];
    double[][][] grid = generateGrid(seed, (int) (width/10)+1, (int) (height/10)+1);
    double max = Double.MIN_VALUE;
    for (int i = 0; i < map.length; i++) {
        for (int j = 0; j < map[0].length; j++) {
            double p = perlinNoise(((double) i) / 10, ((double) j) / 10, grid);
            map[i][j] = p;
            max = (max < p) ? p : max;
        }
    }
    for (int i = 0; i < map.length; i++) {
        for (int j = 0; j < map[0].length; j++) {
            map[i][j] = map[i][j] / max;
        }
    }
    return map;
}

private static double perlinNoise(double x, double y, double[][][] grid) {
    int x0 = ((x < 0) ? (int) x-1 : (int) x);
    int y0 = ((y < 0) ? (int) y-1 : (int) y);
    int x1 = x0 + 1;
    int y1 = y0 + 1;

    double tx = x - (double)x0;
    double ty = y - (double)y0;
    double wx = tx*tx*(3-2*tx);
    double wy = ty*ty*(3-2*ty);

    double l0, l1, ix, iy, p;
    l0 = dotGrid(x0, y0, x, y, grid);
    l1 = dotGrid(x1, y0, x, y, grid);
    ix = cerp(l0, l1, wx);
    l0 = dotGrid(x0, y1, x, y, grid);
    l1 = dotGrid(x1, y1, x, y, grid);
    iy = cerp(l0, l1, wx);
    p = cerp(ix, iy, wy);

    return p;
}

private static double lerp(double a, double b, double w) {
    return (1.0f - w)*a + w*b;
}

private static double cerp(double a, double b, double w) {
    double ft = w * 3.1415927;
    double f = (1 - Math.cos(ft)) * 0.5;
    return  a*(1-f) + b*f;
}

private static double dotGrid(int i, int j, double x, double y, double[][][] grid) {
    double dx = x - i;
    double dy = y - j;
    return (dx*grid[i][j][0] + dy*grid[i][j][1]);

}

private static double[][][] generateGrid(long seed, int width, int height) {
    Random r = new Random(seed);
    double[][][] grid = new double[width][height][2];
    for (int i = 0; i < grid.length; i++) {
        for (int j = 0; j < grid[0].length; j++) {
            double x = r.nextFloat();
            double y = r.nextFloat();
            double v = Math.sqrt((x*x) + (y*y));
            grid[i][j][0] = x/v;
            grid[i][j][1] = y/v;
        }
    }
    return grid;
}

For those that want to test my code i'll also include my rendering method:

private void drawMap(double[][] map, Graphics g) {
    for (int i = 0; i < map.length; i++) {
        for (int j = 0; j < map[0].length; j++) {
            float d = (float) Math.abs(map[i][j]);
            d = (d > 1.0f) ? 1.0f : d;
            Color c = new Color(d, d, d);
            g.setColor(c);  
            g.fillRect(i, j, 1, 1);
        }
    }
}

I hope someone can tell me why i get these strange maze-like structures in my noise and how to get rid of them.

Sources:

http://en.wikipedia.org/wiki/Perlin_noise

http://webstaff.itn.liu.se/~stegu/TNM022-2005/perlinnoiselinks/perlin-noise-math-faq.html


Solution

  • There are two minor problems which add up to give you the interesting pattern you see. I tested things in C with a grid size of 21x21, output size of 200x200 and a random seed of 1234, but things should work equally well in Java.

    My original image for comparison (different pattern due to the seed, but basically the same as yours):

    original

    The first issue is in generateGrid() due to your choice of initial random values. By using nextFloat() you are limited to values from 0 to 1 instead of the desired range of -1 to 1. Fixing this is simple:

    double x = r.nextFloat()*2.0 - 1.0;
    double y = r.nextFloat()*2.0 - 1.0;
    

    and should get you something like:

    fixed 1

    This is an interesting pattern and could be useful for some cases but is not the "normal" Perlin noise. The next issue is your scaling of the map value into a colour. You are taking the absolute value but I believe a better method is to shift up and rescale in the generateMap() function like (not tested):

    double max = Double.MIN_VALUE;
    double min = Double.MAX_VALUE;
    
    for (int i = 0; i < map.length; i++) {
        for (int j = 0; j < map[0].length; j++) {
            double p = perlinNoise(((double) i) / 10, ((double) j) / 10, grid);
            map[i][j] = p;
            max = (max < p) ? p : max;
            min = (min > p) ? p : min;
        }
    }
    
    for (int i = 0; i < map.length; i++) {
        for (int j = 0; j < map[0].length; j++) {
            map[i][j] = (map[i][j] - min) / (max - min); //Limit 0 to 1
        }
    }
    

    which should limit the map values to values of 0-1 which makes your output code much simpler as well. This results in a much better Perlin noise output:

    fixed 2

    I think this is "normal" Perlin noise, at least I couldn't see any other issues and couldn't get anything better through testing. To get "better" noise you'll have to add multiple noise frequencies together.

    Another noise value to image colour scaling function I've seen for Perlin noise is to just take -1.0 to be black and 1.0 to be white which gives you something like:

    fixed 3

    which is basically the same as the last one but a little less contrast. Really how you scale the noise value to a colour is up to you. A great resource for Perlin noise is the LibNoise library. It is C++ but is easily read and has lots of resources.