2D Simplex noise infinite terrain generation

Hello!

Long time since my last question (i hope this is good). ;D
I’m struggling these days to achive a infinite map generation in a 2D top-down game. I’ve tried many noise algorythms, and finally i’ve sticked with matheus23’s implementation from his utils library (https://github.com/matheus23/Utils), more exactly, with his SimplexNoiseLazy2.

The problem i have is the terrain continuity.
As you can see in the next pictures, if i call the get method from the noise with just x and y (tile’s coordinates in chunk (from 0 to 15)), i get a scattered terrain, somehow random.
If i call the get method with x / A and y / A (A is a float), i get a smooth terrain, but it is broken, but rellatively correct. What i see is that A represents the width and the height of the generated noise.

P.S.: In both images i used 8 octaves and smoothness 2.

Can anyone help me with this problem?

I’m not clear what the question is.

8 octaves seems like overkill, but shouldn’t hurt anything as long as the component octaves are properly balanced. Are you doing a “fractal” expansion (what Ken Perlin calls “Sum 1/f Noise”)?

I’ve been working on and off on a Perlin noise visualizer that can be run as an Applet, it you want to take a look at some of the issues around coding a terrain generator. There’s a cringe worthy tutorial (I need to rewrite, or find someone with more writing skills to do it for the project) with a section on terrain. If you call up the visualizer, the menu bar has a “Tutorial” option under the “View” option.

http://hexara.com/SimplexBuilder.html

How many layers of topography are you supporting? In my example, I was mapping to a ColorMap with 7 or 8 different colors and 256 gradations total between them. Looks like you are using much less than that. I also decided to skip octaves in my example, as it seemed like using 1, 4, 16, 64 was actually a decent map. There’s code examples, but I can’t seem to copy and paste from it. (Add THAT to the queue of things to fix. :stuck_out_tongue: )

The “final” terrain image from the SiVi tool is scrolled to the right. (Is there a way to scale an image in the supplied link, or do I have to do that at my end where it is posted?) I think there’s room for improvement with the color mapping, but the general ideas are there.

http://hexara.com/Images/TerraMapSiVi.JPG

[quote]As you can see in the next pictures, if i call the get method from the noise with just x and y (tile’s coordinates in chunk (from 0 to 15))…
[/quote]
That would be your problem.

Use the same Simplex Noise for ALL chunks, and translate the position of each tile to its actual position in the world.

ie: [icode]int realX = tileInChunkX + chunkX*chunkSize;[/icode]

Where realX is passed as the position to the noise generator, tileInChunkX is the tile position from 0 to 15, chunkX is the chunk’s position in chunk units, and chunkSize is the size of each chunk (in this case 16).

@philfrei: My problem is not the noise generation, but its continuity.

@HeroesGraveDev: Thanks for the reply.

I am generating ech chunk with a double for loop (for tileX in chunk and for tileY in chunk), then I create a new Tile object using as its id a value based on the value returned by the noise get method:


//Chunk generation code
public void generate() {
    for(int tileY = 0; tileY < SIZE; tileY++) {
        for(int tileX = 0; tileX < SIZE; tileX++) {
            tiles[tileY * SIZE + tileX] = new Tile(this, map.getGenerator().get(tileX + offsetX, tileY + offsetY));
        }
    }
}

//MapGenerator get method
public int get(int x, int y) {
    float noiseValue = simplexNoise.get(x, y);

    if(noiseValue > 0) {
        if(noiseValue <= 0.2f) {
            return TileType.SAND.id;
        } else if(noiseValue <= 0.4f) {
            return TileType.GRASS.id;
        } else if(noiseValue <= 0.6) {
            return TileType.TREE.id;
        } else if(noiseValue <= 0.8) {
            return TileType.DIRT.id;
        } else {
            return TileType.STONE.id;
        }
    }
    return TileType.WATER.id;
}

:persecutioncomplex: :persecutioncomplex:

It might be, that my implementation is broken … ;D

Images right next to the origin look nice:

farther away, though (very, very far away at [icode]noise.get(x+100000000, y+1000000000)[/icode]) it looks, well… broken:

I’m going to fix this. Or at least try…

It looks like you’re sampling noise at integer locations…that will yield close to white noise (just random numbers).

@matheus23: don’t bother…movies did just fine with an input domain on [0,255] for decades.

I tried your hashing algorithm, but :frowning: The noise always gets some strange characteristics when it’s at far positions.
More specifically, the algorithm from here: http://www.java-gaming.org/topics/simplex-noise-3d/23962/view.html
But it’s hard to ‘port it to two dimensions’ and ‘port it from int’s to long’s’ :confused:

Anyways, @marc, if you try to use x and y offset as seeds, better try giving my implementation a seed. That works better :slight_smile:
If you still have problems, tell me at what x and y positions you query the SimplexNoise :slight_smile:

There are two hashing functions, the simple one is awful but works good enough for me. If you’re having problems with the other…then do you have some coordinates and scale values for me? Distance shouldn’t matter for that hash.

@matheus23 You say that i shoult try making a new seed for each Tile and set its seed to offsetX * x + offsetY or something like this? I don’t really understand what you said in your last post. Sorry… :smiley:

As Roquen said, if you use integers as your x & y inputs, you will be getting who-knows-what because the numbers will be wider than the gradient points.

Continuity will come with two things:

  1. make the x & y of your tile equal to a fraction, e.g. “scale” it. I’m not sure what the best scaling fraction is for Mattheus’s implementation, but for SiVi, 1/128 or 1/256 work pretty well.

  2. treat the (x, y) within tiles as if they are continuations of the first tile, e.g., for a second tile to the left of the first tile, treat x within that tile as x + width-of-first-tile, and continue doing so for each tile.

For example, a tile 4 to the left of the origin would be noise((x + 3 * tileWidth)/128, y/128)

If the inputs to the noise function are doubles, you should be able to “translate” (the part that is added to the x or y) pretty darn far without running into problems.

Wow :o, I just found the problem. I was using as intrepolation equation 3 * t^2 + 2 * t^3 instead of 6 * t3 * t * t - 15 * t3 * t + 10 * t3. I can’t belive it makes such a difference. Although, thanks everyone for help. Now I can continue developing ^^.

P.S. : @matheus23, you have one small issue with your noise: when you use negative numbers, it messes up.
The way I fixed it was to change this code from SimplexNoiseLayerLazy get method:


final int cellX0 = ((int) x / density);
final int cellY0 = ((int) y / density);
final int cellX1 = ((int) x / density) + 1;
final int cellY1 = ((int) y / density) + 1;

with


final int cellX0 = GameMath.floor(x / density);
final int cellY0 = GameMath.floor(y / density);
final int cellX1 = GameMath.floor(x / density) + 1;
final int cellY1 = GameMath.floor(y / density) + 1;

:frowning:

Yes… it makes a difference if you use
3 * t² + 2 * t³
instead of
3 * t² [b]-[/b] 2 * t ³

:confused:

And thanks for finding the mistake :slight_smile:
Fixed and pushed to repo