Island Generation -- Problem with Duplicates

Started by
16 comments, last by KorangarDev 11 years, 3 months ago

[quote name='MarkS' timestamp='1356662054' post='5014952']
I apologize for that. I've been dealing with something in my personal life that has kind of clouded my judgement today. I shouldn't have posted that; it wasn't helpful. However, since I *did* post it, look at TrickyLogic's changes. They were small, but significant.
[/quote]

It is no problem :D. I am glad you are just willing to help. I hope your personal problem clears up soon buddy :). I am wondering though, do you know how to make the terrain more solid and smooth? It seems very holey and turbulent :p

Advertisement
I am wondering though, do you know how to make the terrain more solid and smooth? It seems very holey and turbulent tongue.png

I wish I did. I would like to use this, but it all looks like eroded islands. :/

[quote name='MarkS' timestamp='1356675730' post='5014998']
I wish I did. I would like to use this, but it all looks like eroded islands. :/
[/quote]

Oh :/. Maybe someone will come to help us :D

Thankyou very much for this! I like the randomness and I love the code but do you know if there is any way to make it less "holey". There are specks of water all over the terrain and what I am going for is more of a solid island surrounded by water

I don't fully grasp the code, I take it the last AddGradientPoint method calls simply generate a heightmap gradient to color-code elevation. If so, try raising the elevation offset by a small constant, like so:


float newValue = 2 * islandValue * mapValue - 1 + 0.1f;

That should raise the island by a bit, and get rid of most specks of water (which are really just parts of land which randomly happen to "dip" under sea level, if my understanding is correct). If that's not enough, try 0.2f, etc... experiment.

[quote name='Riztro' timestamp='1356670442' post='5014972']
It is no problem . I am glad you are just willing to help. I hope your personal problem clears up soon buddy . I am wondering though, do you know how to make the terrain more solid and smooth? It seems very holey and turbulent
[/quote]

I think to achieve this you'll need to modify some parameters of your NoiseMap (it can be configured.. right?). If I recall correctly, basic perlin noise has two parameters, amplitude, which describes how high hills and troughs are, and frequency, which indicates how close those hills and troughs are to each other. Of course, it's a fractal design, so it's not so easy to visualize. Here you can see the terrain looks very rugged, which I suspect is because your NoiseMap is using too many octaves (the more octaves you use, the higher the frequency, which causes a lot of tiny ripples everywhere - lower it and you get a less detailed, but smoother look). Try experimenting with those settings, see what happens when you change such and such..

Procedural generation is really about tweaking until it looks cool, from my experience. Look for configurable settings in your noise classes, and check them out :)

“If I understand the standard right it is legal and safe to do this but the resulting value could be anything.”

I think to achieve this you'll need to modify some parameters of your NoiseMap (it can be configured.. right?). If I recall correctly, basic perlin noise has two parameters, amplitude, which describes how high hills and troughs are, and frequency, which indicates how close those hills and troughs are to each other. Of course, it's a fractal design, so it's not so easy to visualize. Here you can see the terrain looks very rugged, which I suspect is because your NoiseMap is using too many octaves (the more octaves you use, the higher the frequency, which causes a lot of tiny ripples everywhere - lower it and you get a less detailed, but smoother look). Try experimenting with those settings, see what happens when you change such and such..

Procedural generation is really about tweaking until it looks cool, from my experience. Look for configurable settings in your noise classes, and check them out

So I cut the frequency down to 0.0625 and the persistence to 0.01 and the amount of octaves to 3 and 4. With those new values I still have little bumps everywhere. It is very odd. So maybe the problem is with the island mask? IDK, I am not sure. What I would like to ultimately do is have some scattered forests and then the rest could just be flat grass land. Forests would be in the higher range instead of mountains. This game is going to be top down tile based so I am looking more to just generate tiles that clump together on an island :)

So I am guessing that I might just have to write my own noise function of some sorts to get the desired flat effect. Am I right? Or do you think it is easily possible with libnoise?

Thankyou very much for this! I like the randomness and I love the code but do you know if there is any way to make it less "holey". There are specks of water all over the terrain and what I am going for is more of a solid island surrounded by water

You're probably better off writing your own noise function if you want that amount of control. The problem is both with the perlin noise and the island mask. Some fixes that make it a bit better:


//Little program to generate test map with only 0 and 1 tile

#include <iostream>
#include <noise/noise.h>
#include "noiseutils.h"
#include <stdlib.h>
#include <string>
#include <sstream>

#define MAP_SIZE 512

using namespace std;
using namespace noise;

template <typename T> string tostring (T v) {
    ostringstream ss;
    ss << v;
    return ss.str();
}

void generate_island(int iseed) {
    module::Perlin myModule;

    myModule.SetSeed (iseed);
    utils::NoiseMap heightMap;
    utils::NoiseMapBuilderPlane heightMapBuilder;
    heightMapBuilder.SetSourceModule(myModule);
    heightMapBuilder.SetDestNoiseMap(heightMap);
    heightMapBuilder.SetDestSize(MAP_SIZE, MAP_SIZE);
    heightMapBuilder.SetBounds(2.0, 6.0, 1.0, 5.0);
    heightMapBuilder.Build();

    float islandMap[MAP_SIZE][MAP_SIZE];
    srand(iseed);

    for (int count = 0; count < 1; count++ ){
        //Initialize values for a new map
        for (int i = 0; i < MAP_SIZE; i++) {
            for (int j = 0; j < MAP_SIZE; j++) {
                islandMap[j][i] = 0;
            }
        }

        //Choose a random location, travel in random directions
        int x = 0, y = 0;
        int sx = 0, sy = 0;
        int size = MAP_SIZE / 2;
        int distance = 0;

        for (int i = 0; i < 1000000; i++) {
            int dx = x - sx;
            int dy = y - sy;
            distance = dx * dx + dy * dy;
            if((y > MAP_SIZE - 1) || y < 0 || (x > MAP_SIZE - 1) || x < 0 || distance > 5000) {
                x = rand() % size / 2 + rand() % size / 2 + MAP_SIZE / 2 - size / 2;
                y = rand() % size / 2 + rand() % size / 2 + MAP_SIZE / 2 - size / 2;
                sx = x;
                sy = y;
            }

            ++islandMap[x][y];

            switch(rand() % 4) {
                case 0:
                    ++x;
                    break;
                case 1:
                    --y;
                    break;
                case 2:
                    --x;
                    break;
                case 3:
                    ++y;
                    break;
                default:
                    break;
            }
        }

        //find max value for normalizing
        for (int i = 0; i < MAP_SIZE; i++) {
            for (int j = 0; j < MAP_SIZE; j++) {
                islandMap[i][j] = islandMap[i][j] * 2 - 1;
            }
        }

        int max = 0;
        int min = 0;
        for (int i = 0; i < MAP_SIZE; i++) {
            for (int j = 0; j < MAP_SIZE; j++) {
                if(max < islandMap[i][j]) {
                    max = islandMap[i][j];
                }

                if(min > islandMap[i][j]) {
                    min = islandMap[i][j];
                }
            }
        }

        //Multiply values of original by new maps generated
        for (int i = 0; i < MAP_SIZE; i++) {
            for (int j = 0; j < MAP_SIZE; j++) {
                float islandValue = (islandMap[i][j] - min) / (max - min);
                float mapValue = (heightMap.GetValue(i, j) + 1) / 2;
                float newValue = 2 * islandValue * mapValue - 1;
                heightMap.SetValue(i, j, newValue);
            }
        }
    }

    utils::RendererImage renderer;
    utils::Image image;
    renderer.SetSourceNoiseMap (heightMap);
    renderer.SetDestImage (image);
    renderer.ClearGradient ();
    renderer.AddGradientPoint (-1.0000, utils::Color (  0,   0, 255, 255)); // shallow
    renderer.AddGradientPoint (-0.9701, utils::Color (  0,   0, 255, 255)); // shallow
    renderer.AddGradientPoint (-0.9700, utils::Color (  0, 128, 255, 255)); // shore
    renderer.AddGradientPoint (-0.9690, utils::Color (  0, 128, 255, 255)); // shore
    renderer.AddGradientPoint (-0.9689, utils::Color (240, 240,  64, 255)); // sand
    renderer.AddGradientPoint (-0.9601, utils::Color (240, 240,  64, 255)); // sand
    renderer.AddGradientPoint (-0.9600, utils::Color ( 32, 160,   0, 255)); // grass
    renderer.AddGradientPoint (-0.4991, utils::Color ( 32, 160,   0, 255)); // grass
    renderer.AddGradientPoint (-0.4990, utils::Color ( 0, 0,   0, 255)); // pines
    renderer.AddGradientPoint (-0.4987, utils::Color ( 0, 0,   0, 255)); // pines
    renderer.AddGradientPoint (-0.3986, utils::Color (96, 63,   0, 255)); // dirt
    renderer.AddGradientPoint ( 0.0000, utils::Color (96, 63,   0, 255)); // dirt
    renderer.AddGradientPoint ( 0.0001, utils::Color (128, 128, 128, 255)); // rock
    renderer.AddGradientPoint ( 1.0000, utils::Color (128, 128, 128, 255)); // rock
    renderer.EnableLight ();
    renderer.SetLightContrast (3.0);
    renderer.SetLightBrightness (2.0);
    renderer.Render ();

    utils::WriterBMP writer;
    writer.SetSourceImage (image);
    writer.SetDestFilename ("tutorial" + tostring(iseed) + ".bmp");
    writer.WriteDestFile ();
}

int main() {
    for(int i=0; i<10; ++i){
        generate_island(i);
    }
    return 0;
}

The island mask consists of many random walks added together. The random walks scale poorly with size of the map (change the MAP_SIZE to see for yourself) because they tend to diverge very quickly. With the above code I've applied two methods two remedy this a bit:

  1. Terminate any random walk if they get beyond a certain distance away from their origin.
  2. Make the starting point of any random walk more likely to be in the middle with a triangular distribution and within a smaller region (controlled with the 'size' variable).

Play a bit with the numbers to see if you can improve the results.

If you want to try something else try this instead: http://en.wikipedia.org/wiki/Diamond-square_algorithm If you set the first N levels yourself instead of randomly generating them you get much more control over the final output.


Thankyou very much for this! I like the randomness and I love the code but do you know if there is any way to make it less "holey". There are specks of water all over the terrain and what I am going for is more of a solid island surrounded by water


You're probably better off writing your own noise function if you want that amount of control. The problem is both with the perlin noise and the island mask. Some fixes that make it a bit better:

//Little program to generate test map with only 0 and 1 tile#include #include #include "noiseutils.h"#include #include #include #define MAP_SIZE 512using namespace std;using namespace noise;template  string tostring (T v) {    ostringstream ss;    ss << v;    return ss.str();}void generate_island(int iseed) {    module::Perlin myModule;    myModule.SetSeed (iseed);    utils::NoiseMap heightMap;    utils::NoiseMapBuilderPlane heightMapBuilder;    heightMapBuilder.SetSourceModule(myModule);    heightMapBuilder.SetDestNoiseMap(heightMap);    heightMapBuilder.SetDestSize(MAP_SIZE, MAP_SIZE);    heightMapBuilder.SetBounds(2.0, 6.0, 1.0, 5.0);    heightMapBuilder.Build();    float islandMap[MAP_SIZE][MAP_SIZE];    srand(iseed);    for (int count = 0; count < 1; count++ ){        //Initialize values for a new map        for (int i = 0; i < MAP_SIZE; i++) {            for (int j = 0; j < MAP_SIZE; j++) {                islandMap[j][i] = 0;            }        }        //Choose a random location, travel in random directions        int x = 0, y = 0;        int sx = 0, sy = 0;        int size = MAP_SIZE / 2;        int distance = 0;        for (int i = 0; i < 1000000; i++) {            int dx = x - sx;            int dy = y - sy;            distance = dx * dx + dy * dy;            if((y > MAP_SIZE - 1) || y < 0 || (x > MAP_SIZE - 1) || x < 0 || distance > 5000) {                x = rand() % size / 2 + rand() % size / 2 + MAP_SIZE / 2 - size / 2;                y = rand() % size / 2 + rand() % size / 2 + MAP_SIZE / 2 - size / 2;                sx = x;                sy = y;            }            ++islandMap[x][y];            switch(rand() % 4) {                case 0:                    ++x;                    break;                case 1:                    --y;                    break;                case 2:                    --x;                    break;                case 3:                    ++y;                    break;                default:                    break;            }        }        //find max value for normalizing        for (int i = 0; i < MAP_SIZE; i++) {            for (int j = 0; j < MAP_SIZE; j++) {                islandMap[i][j] = islandMap[i][j] * 2 - 1;            }        }        int max = 0;        int min = 0;        for (int i = 0; i < MAP_SIZE; i++) {            for (int j = 0; j < MAP_SIZE; j++) {                if(max < islandMap[i][j]) {                    max = islandMap[i][j];                }                if(min > islandMap[i][j]) {                    min = islandMap[i][j];                }            }        }        //Multiply values of original by new maps generated        for (int i = 0; i < MAP_SIZE; i++) {            for (int j = 0; j < MAP_SIZE; j++) {                float islandValue = (islandMap[i][j] - min) / (max - min);                float mapValue = (heightMap.GetValue(i, j) + 1) / 2;                float newValue = 2 * islandValue * mapValue - 1;                heightMap.SetValue(i, j, newValue);            }        }    }    utils::RendererImage renderer;    utils::Image image;    renderer.SetSourceNoiseMap (heightMap);    renderer.SetDestImage (image);    renderer.ClearGradient ();    renderer.AddGradientPoint (-1.0000, utils::Color (  0,   0, 255, 255)); // shallow    renderer.AddGradientPoint (-0.9701, utils::Color (  0,   0, 255, 255)); // shallow    renderer.AddGradientPoint (-0.9700, utils::Color (  0, 128, 255, 255)); // shore    renderer.AddGradientPoint (-0.9690, utils::Color (  0, 128, 255, 255)); // shore    renderer.AddGradientPoint (-0.9689, utils::Color (240, 240,  64, 255)); // sand    renderer.AddGradientPoint (-0.9601, utils::Color (240, 240,  64, 255)); // sand    renderer.AddGradientPoint (-0.9600, utils::Color ( 32, 160,   0, 255)); // grass    renderer.AddGradientPoint (-0.4991, utils::Color ( 32, 160,   0, 255)); // grass    renderer.AddGradientPoint (-0.4990, utils::Color ( 0, 0,   0, 255)); // pines    renderer.AddGradientPoint (-0.4987, utils::Color ( 0, 0,   0, 255)); // pines    renderer.AddGradientPoint (-0.3986, utils::Color (96, 63,   0, 255)); // dirt    renderer.AddGradientPoint ( 0.0000, utils::Color (96, 63,   0, 255)); // dirt    renderer.AddGradientPoint ( 0.0001, utils::Color (128, 128, 128, 255)); // rock    renderer.AddGradientPoint ( 1.0000, utils::Color (128, 128, 128, 255)); // rock    renderer.EnableLight ();    renderer.SetLightContrast (3.0);    renderer.SetLightBrightness (2.0);    renderer.Render ();    utils::WriterBMP writer;    writer.SetSourceImage (image);    writer.SetDestFilename ("tutorial" + tostring(iseed) + ".bmp");    writer.WriteDestFile ();}int main() {    for(int i=0; i<10; ++i){        generate_island(i);    }    return 0;}.h>
The island mask consists of many random walks added together. The random walks scale poorly with size of the map (change the MAP_SIZE to see for yourself) because they tend to diverge very quickly. With the above code I've applied two methods two remedy this a bit:
  • Terminate any random walk if they get beyond a certain distance away from their origin.
  • Make the starting point of any random walk more likely to be in the middle with a triangular distribution and within a smaller region (controlled with the 'size' variable).
Play a bit with the numbers to see if you can improve the results.

If you want to try something else try this instead: http://en.wikipedia.org/wiki/Diamond-square_algorithm If you set the first N levels yourself instead of randomly generating them you get much more control over the final output.

Here are my results when I put it in my engine! Looks good! biggrin.png Thanks a ton!!! biggrin.png

All /I/ was saying was that if your problem is division, an easy hack would be to divide by a number that couldn't carry the problem over.

Really though, you want to be able to get it working with any size. Still it might help to try it in certain situations, to find out if that is the problem.

But I'm really doubting it is. Listen to these guys, they know better than me it looks like.

I could help you build one that kicks ass, well the general design not so much specific implementation, but fixing one isn't my area of expertise. I think in code but I still have trouble reading it.

This topic is closed to new replies.

Advertisement