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

2 Answers 2

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\$
11
  • \$\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\$ Sep 9, 2013 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\$ Sep 9, 2013 at 15:48
  • \$\begingroup\$ What prime numbers are you referring to? \$\endgroup\$ Sep 9, 2013 at 15:54
  • \$\begingroup\$ The line return (float) ( 1.0 - ( (x * (x * x * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824.0); \$\endgroup\$ Sep 9, 2013 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\$ Sep 9, 2013 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\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .