1

I'm working on a tile based game and already got a generator working. But it doesn't seem to be smooth enough to look realistic.

Here's the code I'm using:

for (int i = 0; i < width; i++)
        {
            float y = ClassNoise.PerlinNoise1D(i, 2f, 4);
for(int j = 2048+y;j<height;j++)
{
  tiles[i][j] = new Tile(i,j);
}
}

public class ClassNoise
{
    public static float Noise(int x)
    {
        x = (x<<13) ^ x;
        return (float) ( 1.0 - ( (x * (x * x * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824.0);   
    }
    public static float PerlinNoise1D(float x, float persistence, int octaves)
    {
        float total = 0;
        float p = persistence;
        int n = octaves - 1;

        for (int i = 0; i <= n; i++)
        {

            float frequency = (float) Math.pow(2, i);
            double amplitude = Math.pow(p, i);
            total += InterpolatedNoise(x * frequency) * amplitude;
        }

        return (int) total;

    }

    private static float InterpolatedNoise(float x)
    {
        int integer_X = (int) x;
        float fractional_X = x - integer_X;

        float v1 = SmoothNoise1D(integer_X);
        float v2 = SmoothNoise1D(integer_X + 1);

        return CosineInterpolate(v1, v2, fractional_X);

    }

    public static float CosineInterpolate(float a, float b, float x)
    {
        float ft = (float) (x * Math.PI);
        float f = (float) ((1 - Math.cos(ft)) * 0.5);

        return a * (1 - f) + b * f;
    }
    public static float SmoothNoise1D(int x)
    {
        return Noise(x)/2  +  Noise(x-1)/4  +  Noise(x+1)/4;
    }

}

How can I make this terrain generator smoother?

EDIT:

It currently looks like this (The terrain on the screenshot isn't the full terrain it's just the terrain on the screen): Terrain

user1990950
  • 139
  • 1
  • 11
  • How about a screenshot of what it looks like now? Typically, if you want smoother, you need smaller tiles, or you need to change your tile textures. – House Sep 09 '13 at 14:35
  • Added a screenshot of the terrain – user1990950 Sep 09 '13 at 14:57
  • Ha, yeah, I guess that's not very smooth :). – House Sep 09 '13 at 14:58
  • That's why I'm asking for a solution ;) – user1990950 Sep 09 '13 at 14:59
  • 1
    Any chance you can explain your approach in words instead of a code dump? – SpartanDonut Sep 09 '13 at 15:16
  • Well, I just want smoother terrain (e.g. hills). It's not very smooth right now. – user1990950 Sep 09 '13 at 15:20
  • can you post an example of desired terrain? "Smooth" can be different things to different people. if you want the peaks to be lower or the valleys to be higher, how much? How tall of a peak is too tall? how low is too low? Should the next peak be within a certain distance of the previous block or is there just a general "max" height? – wes Sep 09 '13 at 15:30

2 Answers2

5

Primarily the issue is that you are using a non realistic persistence of 2. Persistence should be between 0 and 1, where closer to 1 is more rough and closer to 0 is more smooth, see here for more details. 2 is simply a meaningless number that more or less says make the finest octave unreasonably large. Larger than the maximum output is supposed to be.

Correcting this will cause you to have a continuos 0 output, this is because you have cast your output to (int), stop doing this and you will start getting an output between -1 and 1 that is smooth. The following graph is for values between 0 and 20 with persistance of 0.5.

enter image description here

Multiple octaves

You are also effectively using the same seed for all your octaves, each octave uses the same 3 prime numbers in the Noise function so actually your octaves are the same noise at different scales. This can cause difficulties. Each octave should have its own primes.

Replace your Noise function with something like:

    public float getBasicNoise(int x) {
        x=(x<<13)^x; // bitwise shift to the left by 13 places then rased to n
    //&amp; performs a bitwise multiplication (i.e. 0*0=0, 1*0=0, 1*1=1
    //it makes this multiplication with the largest possible int
    //i.e. +111111.....1111
    return (float)( 1.0 - ( (x * (x * x * primes[2] + primes[0]) + primes[1]) &amp; Integer.MAX_VALUE) / 1073741824f);   

}

Then each octave is initialised with its own primes, avoiding these problems.

ice1000
  • 286
  • 3
  • 19
Richard Tingle
  • 634
  • 3
  • 15
  • This works really well, the only problem is "for(int j = 2048+y;j<height;j++)". The y variable is a float. But I need an integer for it to work. Casting wouldn't work because I'd always get 0. – user1990950 Sep 09 '13 at 15:47
  • Multiply the output by some value (say 100 or whatever number to get your desired range) then cast to int – Richard Tingle Sep 09 '13 at 15:48
  • What prime numbers are you referring to? – user1990950 Sep 09 '13 at 15:54
  • The line return (float) ( 1.0 - ( (x * (x * x * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824.0); – Richard Tingle Sep 09 '13 at 15:55
  • @user1990950 This will of course make it somewhat nasty as you've currently written it, I'd put in in a more object orientated frame work, so you create a Perlin noise object that you initialise with 3 primes and is resonsible for creating a single octave of noise, a Fractal noise object that is built up of several octaves or perlin noise objects and then poll that object for noise values – Richard Tingle Sep 09 '13 at 16:00
  • Okay, now it's working really well. But one problem is still there: The "hills" of the terrain aren't tall enough. Is it possible to make them taller? – user1990950 Sep 09 '13 at 16:02
  • @user1990950 I assume simply scaling the terrain isn't an option? – Richard Tingle Sep 09 '13 at 16:04
  • No, it's going higher/lower 1-3 tiles on every x-change and I like this. But the max height/depth isn't enough. (I'm using this code: ClassNoise.PerlinNoise1D(i, 0.50f, 2)*4;) – user1990950 Sep 09 '13 at 16:08
  • Ah, you want to create a really long range variation as well. At present all your octaves go between some range and 0, so beween 2^4 blocks and 1 block, but you want to add that to one that perhaps has octaves between 2^8 and 2^6; missing out the short range octaves. Then you add your long range variation (which has high scaling) to your short range variation (which has small scaling). But at that point it really is just trying it and seeing if you like what you're getting – Richard Tingle Sep 09 '13 at 16:16
  • Like this? float y = ClassNoise.PerlinNoise1D(i, 0.5f, 8)*4; public static float PerlinNoise1D(float x, float persistence, int octaves) { float total = 0; float p = persistence; int n = octaves - 1;

    for (int i = 6; i <= n; i++) {

    float frequency = (float) Math.pow(2, i); double amplitude = Math.pow(p, i); total += InterpolatedNoise(x * frequency) * amplitude; }

    return total;

    }

    – user1990950 Sep 09 '13 at 16:22
0

You might consider using the midpoint displacement algorithm vice perlin noise depending on the terrain you'd like to have

Why would someone chose midpoint displacement over perlin noise for 3D terrain generation?

Here is a great example of "hills" generated using midpoint displacement algorithm: https://gamedev.stackexchange.com/a/9386/20399

What is the simplest method to generate smooth terrain for a 2d game?

wes
  • 305
  • 1
  • 10