0
\$\begingroup\$

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

\$\endgroup\$
  • \$\begingroup\$ 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. \$\endgroup\$ – MichaelHouse Sep 9 '13 at 14:35
  • \$\begingroup\$ Added a screenshot of the terrain \$\endgroup\$ – user1990950 Sep 9 '13 at 14:57
  • \$\begingroup\$ Ha, yeah, I guess that's not very smooth :). \$\endgroup\$ – MichaelHouse Sep 9 '13 at 14:58
  • \$\begingroup\$ That's why I'm asking for a solution ;) \$\endgroup\$ – user1990950 Sep 9 '13 at 14:59
  • 1
    \$\begingroup\$ Any chance you can explain your approach in words instead of a code dump? \$\endgroup\$ – SpartanDonut Sep 9 '13 at 15:16
4
\$\begingroup\$

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

        //& performs a bitwise multiplication (i.e. 0*0 =0, 1*0=0, 1*1=1
        //it makes this multiplication with the largerst possible int
        //i.e. +111111.....1111
        return (float)( 1.0 - ( (x * (x * x * primes[2] + primes[0]) + primes[1]) & Integer.MAX_VALUE) / 1073741824f);   

    }

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

\$\endgroup\$
  • \$\begingroup\$ 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. \$\endgroup\$ – user1990950 Sep 9 '13 at 15:47
  • \$\begingroup\$ Multiply the output by some value (say 100 or whatever number to get your desired range) then cast to int \$\endgroup\$ – Richard Tingle Sep 9 '13 at 15:48
  • \$\begingroup\$ What prime numbers are you referring to? \$\endgroup\$ – user1990950 Sep 9 '13 at 15:54
  • \$\begingroup\$ The line return (float) ( 1.0 - ( (x * (x * x * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824.0); \$\endgroup\$ – Richard Tingle Sep 9 '13 at 15:55
  • \$\begingroup\$ @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 \$\endgroup\$ – Richard Tingle Sep 9 '13 at 16:00
0
\$\begingroup\$

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?

\$\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.