Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Strange result from perlin noise generator

Tags:

java

noise

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

like image 527
Exevan Avatar asked Oct 28 '14 01:10

Exevan


People also ask

What does Perlin noise return?

PerlinNoise returns a “float Value between 0.0 and 1.0” yet warns that “it is possible for the return value to slightly exceed 1.0f”.

Is Perlin noise random?

Perlin noise is a procedural texture primitive, a type of gradient noise used by visual effects artists to increase the appearance of realism in computer graphics. The function has a pseudo-random appearance, yet all of its visual details are the same size.

Does Minecraft use Perlin or simplex noise?

Minecraft is specifically using Perlin noise calculations, like the kind you'd use to create a rough-looking texture for a 3D model. It starts out on a very broad level, painting a basic topographical map, and adds "noise" through finer terrain details like lakes, shrubbery and animals.

Is Perlin noise normally distributed?

But a perlin noise is normal distributed (ranges from -1 to 1, mean at 0) so it's not that easy.


2 Answers

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.

like image 94
uesp Avatar answered Oct 30 '22 17:10

uesp


Keep in mind that Perlin noise is an older algorithm that, even with the fixes provided by uesp, still exhibits visually significant grid artifacts by way of lining all of its features up with the cardinal axes and 45-degree diagonals. In my opinion there's little reason to use Perlin noise nowadays.

Take a look at something called OpenSimplex noise: https://gist.github.com/KdotJPG/b1270127455a94ac5d19

Comparison shot between Perlin and OpenSimplex (2D slices of the 3D functions)

enter image description here

  • Left is noise(x, y, 0) grayscale
  • Next is noise(x, y, 0) > 0 ? white : black
  • Next is |noise(x, y, 0)| > 0.1 ? white : black
  • Next is noise(x, y, 0.5) grayscale

Note that OpenSimplex noise and Simplex noise are two different algorithms. Simplex noise, in most implementations, has a few more artifacts than OpenSimplex, and Simplex in 3D+ is patented.

like image 44
KdotJPG Avatar answered Oct 30 '22 18:10

KdotJPG