Simplex noise for reasonable maps

I was stuck with procedurally generated maps and luckily came across this old post:

http://www.java-gaming.org/index.php?topic=31637.0

However, I don’t understand the “underlying rules” to set parameters in a way that gives macroscopic results. If it doesn’t make sense, let me rephrase: I can’t tell the algorithm to produce a set of islands, or a big continent surrounded by ocean, or many (or few) mountains, etc, because the effect I get from changing slighly the parameters is seemlingly “random” to me.

The code I’m using is pretty much the one kindly provided by longshorts:


public static float[][] generateOctavedSimplexNoise(final int width,
                                                        final int height,
                                                        final int octaves,
                                                        final float roughness,
                                                        final float scale)
    {
        final float[][] totalNoise = new float[width][height];
        float layerFrequency = scale;
        float layerWeight = 1f;
        float weightSum = 0f;

        for (int octave = 0; octave < octaves; octave++)
        {
            // Calculate single layer/octave of simplex noise, then add it to total noise
            for (int x = 0; x < width; x++)
            {
                for (int y = 0; y < height; y++)
                {
                    totalNoise[x][y] += (float) noise(x * layerFrequency, y * layerFrequency) * layerWeight;
                }
            }

            // Increase variables with each incrementing octave
            layerFrequency *= 2f;
            weightSum += layerWeight;
            layerWeight *= roughness;
        }

        for (int x = 0; x < width; x++)
        {
            for (int y = 0; y < height; y++)
            {
                totalNoise[x][y] /= weightSum;
            }
        }

        return totalNoise;
    }

I do however correct the final noise by dividing it by weightSum (the final loop), as otherwise it wouldn’t always be within -1 and 1 as it should.

The parameters I can manipulate are three: number of “octaves”, roughness and scale.

Right now I have the following thresholds (should be clear what the names are):


if (heightMap[x][y] < -0.1)
                {
                    map[x][y] = Cell.DEEP_WATER;
                }
                else if (heightMap[x][y] < 0f)
                {
                    map[x][y] = Cell.WATER;
                }
                else if (heightMap[x][y] < 0.3f)
                {
                    map[x][y] = Cell.GRASS;
                }
                else if (heightMap[x][y] < 0.4f)
                {
                    map[x][y] = Cell.HILL;
                }
                else
                {
                    map[x][y] = Cell.MOUNT;
                }

There are some many “free parameters” that is quite overwhelming.

Could you give an explanation of how to interpret the noise parameters, and possibly some advice on the above thresholds? I’m quite at a loss here.

Odd to see someone using my old code :stuck_out_tongue:

The thresholds should be fairly trivial to understand. The map I would normally generate will create values between -1 and 1 with fairly even distribution (depending on how you have set the noise parameters though, particularly the scale). With your implementation approximately 50% of the map should be covered in water. If there is too much then you just set the value of deep water to less. Tweak the values depending on how you want your map to look. In my game I use a tilemap and I decide what kind of tiles to render depending on the threshold.

In terms of the parameters, my advice is to get some visual output first, then play with the values. How perlin noise generally works is explained here: http://freespace.virgin.net/hugo.elias/models/m_perlin.htm
Note: Persistance is equivalent to roughness if my memory serves me correctly.

To get a large continent surrounded by an ocean, you just need to apply a function to modify the values towards the center of the map to be closer to one, and the cells towards the edges of the map to be closer to 0. I’m not entirely sure how you would implement this however.

How 'bout this:

f(distance) = height/(size^(distance^2))

distance - the distance of one particular point on the island to the island center
height - the total height at distance = 0.0 (with height=1.0 you get 1.0 at distance = 0.0)
size - how wide the island should be; must be larger than 1.0 (bigger values make the island smaller/narrower)
^ - potentiation operator. This is NOT to be confused with Java’s ‘^’ operator, but means Math.pow()

Examples:
f(distance) = 1.0/(1.1^(distance^2)) gives a large island with small slope
f(distance) = 1.0/(5.0^(distance^2)) gives a small island with big slope

Using an online function plotter is great to get a grasp of it.

Ken Perlin describes using an ABS function on the [-1…1] output, and mapping the resulting [0…1] to a color map or color function. With the ABS function, he calls the result “Turbulent” noise.

When [-1…1] is mapped to [0…1] via the function f(x) = (x + 1) / 2, and then applies the color mapping function, then he calls it “Smooth” noise.

To me, the “Turbulent” noise does a better job of making landscapes/maps. There is more activity at the 0 end, and a sort of “crease” or “fold” in the function, and as it heads towards 1, there is an ever decreasing likelihood of occurrence.

Octaves relate to the rate of change. The higher the octave, the more variability to the result. Or another way to think about it, the octave relates to how close the reference poles are to each other. The curvature results from angles being assigned at the poles and lines computed to connect the space so that each pole is intersected at the random angle assigned to it.

When you combine octaves, you are combining (via weights) the differing rates of randomness. If the higher octave is given a higher weight, there will be a finer grain to the randomness. For some reason, the “fractal” progression of weights and functions seems to be one of the most satisfying, including for creating topology. In that case, the weight is inversely proportional to the octave. 1/2 * octave 1 + 1/4 * octave 2 + 1/8 * octave 3, where each octave is 2^n. Adding up octaves weights in this sequence is always safely within the bounds of [0…1] whether using the “smooth” or “turbulent” algos above.

My brain is a little too full right now to closely read the code you posted. I hope my above lines are actually helpful somehow and not off topic.

By the way, the hugo elias article is a useful read, but the noise he describes is not Perlin noise or even gradient noise, if I remember correctly. With gradient noise, random gradients (angles) are assigned to the pole points, not random values. I think each pole point is actually of 0 value but the line or plane going through it is at a random angle.

http://github.prideout.net/