Island Generation -- Problem with Duplicates

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

Hello all,

I have been working for the past few days on a perlin noise island generator (using libnoise). What I do is generate a terrain, and then apply an island mask to it. I have added a seed to the island generation portion but I am getting similiar results each time even with a seed. Can someone help me so that I can generate a truely random island each time? Here is what I am currently getting (seeds in parenthesis):

(1233212)

vwxr8o.jpg

(999555857)

2434x9w.jpg

So, as you can see, the maps are very similar. I really don't know why. If needed, here is my code:

(Steps I take)

1st Generate terrain

2nd Generate island mask

3rd Apply island mask to terrain

4th Draw the terrain


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

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

using namespace std;
using namespace noise;

int main()
{

   module::Perlin myModule;

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

   
   string seed;
   int iseed;
   //Add support for character conversion to ints and clip string to 8 numbers
   cout << "Please enter a seed: ";
   getline(cin,seed);

   cout << seed << endl;
   iseed = atoi(seed.c_str());
   cout << iseed << endl;

   float islandMap[512][512];

   enum directions {LEFT = 0, UP, RIGHT, DOWN, NONE};

   for(int count = 0; count < 1; count++)
   {

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


      //Choose a random location, travel in random directions
      int xRand = 0,yRand=0;
      int xPos = 0,yPos=0;

      srand((iseed*57)/(iseed*17));
      cout << iseed;

      xRand= rand()%512;
      yRand= rand()%512;

      xPos = xRand;
      yPos = yRand;

      islandMap[xRand][yRand] = 1;
      directions direction = NONE;

      srand((int)((iseed*iseed)/57));
      for (int i = 0; i < 10000000; i++) 
      {
         if(xPos == 511 || xPos == 0)
         {
            xPos = rand()%512;
            islandMap[xPos][yPos] +=1;
         }   

         if(yPos == 511 || yPos == 0)
         {
            yPos = rand()%512;
            islandMap[xPos][yPos] +=1;
         }

         int oldValue;

         oldValue = islandMap[xPos][yPos];

         int temp;
         temp = rand()%4;
         direction = (directions)temp;
         if(direction == LEFT)
         {
            xPos-=1;
         }
         else if(direction == UP)
            yPos-=1;
         else if(direction == RIGHT)
            xPos+=1;
         else if(direction == DOWN)
            yPos+=1;

         if(oldValue >= islandMap[xPos][yPos])
         {
            islandMap[xPos][yPos] +=1;
         }


      }

      //find max value for normalizing
      int max = 0;
      int min = 0;
      for (int i = 0; i < 512; i++) 
      {
         for (int j = 0; j < 512; j++) 
         {
            if(max < islandMap[i][j])
               max = islandMap[i][j];

            //cout << 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 < 512; i++) 
      {
         for (int j = 0; j < 512; j++) 
         {
            float islandValue = (islandMap[i][j] - min) / (max-min);
            //cout << islandValue;
            float mapValue = (heightMap.GetValue(i,j) + 1) / (1+1);
            float newValue = (-1 + (2/1)*(islandValue*mapValue));
            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.bmp");
   writer.WriteDestFile ();

   return 0;
}

Thankyou for your help in advance!! :D

Advertisement
Your seed is always the same... Always 57/17.
-What is the reason behind it, I wonder?

[strike]I'm actually puzzeld about your maps being the slightest different.[/strike]
You're calling srand all time. -You should only be calling it once (it keeps the following numbers the same for every time it's invoked with the same seed. The behaviours you're experiencing is familiar to when i did it years ago.

To elaborate on SuperVGA, this line is the line that is fishy:


srand((iseed*57)/(iseed*17));

The iseed in the numerator is cancelled by the iseed in the denominator, causing the seed to always be 57/17, or 3, when the island mask is generated.

57/17 isn't 3, 51/17 is. Depends how big the seed is, and if it overflows or not. Calling srand more than once is dodgy anyway, in this circumstance. The bitmaps aren't the same though, they look slightly different. Only call srand more than once to repeat a previous random sequence.
"Most people think, great God will come from the sky, take away everything, and make everybody feel high" - Bob Marley

I'd rebuild it, piece by piece. That's obviously terrible advice, but its honestly what I would do.


Although I didn't look at your code, you might try this:
You might want to generate a constant perlin noise and a random location, instead of a random perlin noise and a constant location.

Also try adjusting the map maximums to some number which is coprime to the number you're using right now. Avoid Mersenne numbers. If that doesn't work, try again with a number coprime to both of those. You might be generating a specific repeating pattern or something. Well, no, the map would be offset I suppose.

Not sure. Simplify and try to generate more basic structures. Try to make those diverse. Then add more depth.

Change each number one by one dude. It's either a specific algorithm or a specific constant that's throwing you off.

[quote name='Paradigm Shifter' timestamp='1356634870' post='5014793']
57/17 isn't 3, 51/17 is. Depends how big the seed is, and if it overflows or not. Calling srand more than once is dodgy anyway, in this circumstance. The bitmaps aren't the same though, they look slightly different. Only call srand more than once to repeat a previous random sequence.
[/quote]

Actually, 57/17 = 3 here, since we're talking about integers and thus integer division. But well-spotted for the overflow - that's really the only reason why the maps are different. Don't know what the rationale behind those numbers was, though.

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

I just got done playing with his code. I took out the multiple calls to srand (now a single call: srand(iseed);), the do nothing for loop and various other oddities ((2 / 1) ?). It now creates very regular terrains. The generated terrains show a very strong rotational symmetry. Each 1/4 of the image is nearly an exact copy of each other, rotated 90°.

I think the entire algorithm needs to be rewritten.

The algorithm used looks like the same from: http://breinygames.blogspot.nl/2012/06/generating-terrain-using-perlin-noise.html

Just playing around a little (I did change some code so I could more easily read it), here is what I came up with:


//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>

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(512, 512);
    heightMapBuilder.SetBounds(2.0, 6.0, 1.0, 5.0);
    heightMapBuilder.Build();

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

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

        //Choose a random location, travel in random directions
        int x = 0, y = 0;
        float previous = 0;

        for (int i = 0; i < 1000000; i++) {
            if(y == 511 || y == 0 || x == 511 || x == 0) {
                x = rand() % 512;
                y = rand() % 512;
            }   

            if(previous >= islandMap[x][y]) {
                islandMap[x][y] += 1;
            }

            previous = islandMap[x][y];

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

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

        int max = 0;
        int min = 0;
        for (int i = 0; i < 512; i++) {
            for (int j = 0; j < 512; 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 < 512; i++) {
            for (int j = 0; j < 512; 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;
}

Your seed is always the same... Always 57/17.
-What is the reason behind it, I wonder?

I'm actually puzzeld about your maps being the slightest different.
You're calling srand all time. -You should only be calling it once (it keeps the following numbers the same for every time it's invoked with the same seed. The behaviours you're experiencing is familiar to when i did it years ago.
To elaborate on SuperVGA, this line is the line that is fishy:


srand((iseed*57)/(iseed*17));

The iseed in the numerator is cancelled by the iseed in the denominator, causing the seed to always be 57/17, or 3, when the island mask is generated.

Thankyou for pointing that out! biggrin.png I have fixed that but now I am still getting similar maps.. :/. I am sure changing that helps though biggrin.png

I'd rebuild it, piece by piece. That's obviously terrible advice, but its honestly what I would do.


Although I didn't look at your code, you might try this:
You might want to generate a constant perlin noise and a random location, instead of a random perlin noise and a constant location.

Also try adjusting the map maximums to some number which is coprime to the number you're using right now. Avoid Mersenne numbers. If that doesn't work, try again with a number coprime to both of those. You might be generating a specific repeating pattern or something. Well, no, the map would be offset I suppose.

Not sure. Simplify and try to generate more basic structures. Try to make those diverse. Then add more depth.

Change each number one by one dude. It's either a specific algorithm or a specific constant that's throwing you off.

I got lost at your third paragraph :/ Can you please elaborate on the thing that I should also try adjusting smile.png

I just got done playing with his code. I took out the multiple calls to srand (now a single call: srand(iseed);), the do nothing for loop and various other oddities ((2 / 1) ?). It now creates very regular terrains. The generated terrains show a very strong rotational symmetry. Each 1/4 of the image is nearly an exact copy of each other, rotated 90°.

I think the entire algorithm needs to be rewritten.

Sorry yeah the (2/1) thing gets the numbers into the range of -1 and 1. The 1 in that equation is the max number in the set of numbers. And what algorithm needs to be rewritten, the island masking one? :/ Because if that is so, I have no clue what to do. I just followed what this guy said he did on his blog.

The algorithm used looks like the same from: http://breinygames.blogspot.nl/2012/06/generating-terrain-using-perlin-noise.html

Just playing around a little (I did change some code so I could more easily read it), here is what I came up with:



//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>

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(512, 512);
    heightMapBuilder.SetBounds(2.0, 6.0, 1.0, 5.0);
    heightMapBuilder.Build();

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

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

        //Choose a random location, travel in random directions
        int x = 0, y = 0;
        float previous = 0;

        for (int i = 0; i < 1000000; i++) {
            if(y == 511 || y == 0 || x == 511 || x == 0) {
                x = rand() % 512;
                y = rand() % 512;
            }   

            if(previous >= islandMap[x][y]) {
                islandMap[x][y] += 1;
            }

            previous = islandMap[x][y];

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

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

        int max = 0;
        int min = 0;
        for (int i = 0; i < 512; i++) {
            for (int j = 0; j < 512; 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 < 512; i++) {
            for (int j = 0; j < 512; 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;
}

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 tongue.png


I just got done playing with his code. I took out the multiple calls to srand (now a single call: srand(iseed);), the do nothing for loop and various other oddities ((2 / 1) ?). It now creates very regular terrains. The generated terrains show a very strong rotational symmetry. Each 1/4 of the image is nearly an exact copy of each other, rotated 90°.

I think the entire algorithm needs to be rewritten.

Sorry yeah the (2/1) thing gets the numbers into the range of -1 and 1. The 1 in that equation is the max number in the set of numbers. And what algorithm needs to be rewritten, the island masking one? :/ Because if that is so, I have no clue what to do. I just followed what this guy said he did on his blog.



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.

This topic is closed to new replies.

Advertisement