2
\$\begingroup\$

I am experimenting with generating height maps using radial gradients and perlin noise, using a technique I found here: http://www.reddit.com/r/gamedev/comments/1g4eae/need_help_generating_an_island_using_perlin_noise/cagmlsi.

This is my code:

 public override void generate(int seed) {
        Random rand = new Random(seed);


        float[,] pixelMap = new float[gridSizeX, gridSizeY];

        // generate random starting points for the islands
        centers = new int[ISLAND_COUNT,2];
        for (int i = 0; i < ISLAND_COUNT; i++) {
            centers[i, 0] = rand.Next(0, gridSizeX - 1);
            centers[i, 1] = rand.Next(0, gridSizeY - 1);
        }

        float maxDistance = 0;
        for (int x = 0; x < gridSizeX; x++) {
            for (int y = 0; y < gridSizeY; y++) {
                // calculate the distance for each center node to the current coords.
                // pick the distance that is closest for the color value
                float distanceToCenter = 9999999; //artificially high number
                for (int i = 0; i < ISLAND_COUNT; i++) {
                    int centerX = centers[i, 0];
                    int centerY = centers[i, 1];
                    float localDistance;

                    //Simple squaring, you can use whatever math libraries are available to you to make this more readable
                    //The cool thing about squaring is that it will always give you a positive distance! (-10 * -10 = 100)
                    float testX = x;
                    float testY = y;

                    float distanceX = (centerX - testX) * (centerX - testX);
                    float distanceY = (centerY - testY) * (centerY - testY);

                    localDistance = (float)Math.Sqrt(distanceX + distanceY);

                    // let's see if the wrapped distance is shorter; if so use that.
                    // this allows our gradient to wrap along the X axis
                    if (x > centerX) {
                        testX = (gridSizeX - x) + centerX * 2;
                    } else {
                        testX = (gridSizeX + x);
                    }

                    distanceX = (centerX - testX) * (centerX - testX);
                    if (localDistance > (float)Math.Sqrt(distanceX + distanceY)) {
                        localDistance = (float)Math.Sqrt(distanceX + distanceY);
                    }

                    //is this closer than the last recorded distance? If so use it
                    if (localDistance < distanceToCenter) {
                        distanceToCenter = localDistance;
                    }

                    // record the maximum distance calculated
                    if (localDistance > maxDistance) {
                        maxDistance = localDistance;
                    }
                }                    



                // add the color to the pixel map, adjusted for an rgb between 0 and 255.
                pixelMap[x, y] = (distanceToCenter / gridSizeX) * 255 * 2;
            }                    
        }           


        drawImage(pixelMap);


        // save image of final generation
        passOutput.Save(@"test.png", ImageFormat.Png);
    }

This code generates an image like this though:

enter image description here

the red dots (if you can see them) are diagnostics code I added to mark the center of the gradients.

I am completely stumped as to why these white borders are happening at the midway points between gradients. I feel like there is a simple solutions to this, but I have been at this for days and have been unable to fix it no mater what I try.

What I am expecting is something like this mockup made in Photoshop:

enter image description here

Notice how there are no sharp edges; all gradients int he image blend into each-other seamlessly.

Any ideas?

\$\endgroup\$
  • 1
    \$\begingroup\$ Looks like what you have is an F1 Worley basis (en.wikipedia.org/wiki/Worley_noise or libsh.org/posters/worley_poster.pdf) - the gradient ramps up as we move away from each of the seed points, reaching local maxima at the borders of the Voronoi cells. This is a common step in procedural generation, and matches the code you've shown. What were you expecting instead? \$\endgroup\$ – DMGregory Nov 30 '14 at 17:32
  • \$\begingroup\$ DMGregory: I just edited my post with an example of what I would be expecting. It was never my intention to create Voronoi cells. :P \$\endgroup\$ – dmarra Nov 30 '14 at 17:43
3
\$\begingroup\$

Here are four options that you can try:

A) Scale & threshold existing output

You can ensure that the gradient saturates at some maximum value before reaching any of the cell borders. This will tend to make small holes of uniform size, but you can introduce size variation by assigning a random scale factor to each seed point and scaling distances to that point before comparing with its neighbours.

B) Blur existing output

This is self-explanatory. With a wide enough blur kernel, you can smudge the sharp borders into smooth high regions like in your example. This may require some scaling/offsetting to maintain the desired contrast/mins/maxes.

C) Smooth Voronoi

Iñigo Quilez describes a method to generate a Voronoi pattern with smooth borders. The low-power exponential falloff shown in the top-left is fairly similar to your target example: enter image description here

D) Multiply all distances

Rather than outputting the distance to the closest point, return the product of the distances to all of the points. This gradient still hits zero at each seed point, but has no first derivative discontinuities between them (unlike standard Worley F1 noise). This will require some scaling to keep in the desired brightness range.

| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.