9
\$\begingroup\$

I have been working on a 3D game in java using LWJGL for a while now. I am trying to make procedurally generated infinite 3D terrain that is generated around the player.

So far I have:

  • 3D terrain chunks that are loaded and unloaded when the player moves. These chunks are made up of triangles not quads, and are 128x128 vertices.
  • A Perlin noise class that can successfully generate Perlin noise. (I think it is actually something called value noise, but it works)
  • A world class that handles loading and unloading of chunks, and applying the height maps to the chunks.

All of this works as it is coded to do, but not how I want it to. I don't know how to tile the noise seamlessly.

How would I make the noise tile, and furthermore, how would I generate large structures such as mountains that occupy probably up to hundreds of chunks.

Here is what is currently happening with the chunks and noise not tiling:

This

Here is my Perlin noise code:

private float[][] perlinNoise(int width, int height, int octave, float[][] whiteNoise)
{
    float[][] result = new float[width][height];

    int samplePeriod = 1 << octave;
    float sampleFrequency = 1.0f / samplePeriod;

    for (int i = 0; i < width; i++)
    {
        int x1 = (i / samplePeriod) * samplePeriod;
        int x2 = (x1 + samplePeriod) % width;
        float xBlend = (i - x1) * sampleFrequency;

        for (int j = 0; j < height; j++)
        {
            int y1 = (j / samplePeriod) * samplePeriod;
            int y2 = (y1 + samplePeriod) % height;
            float yBlend = (j - y1) * sampleFrequency;

            float top = (float) MathHelper.interpolateLinear(whiteNoise[x1][y1], whiteNoise[x2][y1], xBlend);

            float bottom = (float) MathHelper.interpolateLinear(whiteNoise[x1][y2], whiteNoise[x2][y2], xBlend);

            result[i][j] = (float) MathHelper.interpolateLinear(top, bottom, yBlend);
        }
    }
    return result;
}

public float[][] generatePerlinNoise(int width, int height, Random random, int octaveCount)
{
    float[][] whiteNoise = new float[width][height];
    float[][][] totalNoise = new float[octaveCount][][];
    float[][] perlinNoise = new float[width][height];
    float amplitude = 1.0f;
    float totalAmplitude = 0.0f;
    float persistance = 0.5f;

    for (int i = 0; i < width; i++)
    {
        for (int j = 0; j < height; j++)
        {
            whiteNoise[i][j] = random.nextFloat() % 1;
        }
    }
    for (int i = 0; i < octaveCount; i++)
    {
        totalNoise[i] = perlinNoise(width, height, i, whiteNoise);
    }
    for (int o = octaveCount - 1; o >= 0; o--)
    {
        amplitude *= persistance;
        totalAmplitude += amplitude;

        for (int i = 0; i < width; i++)
        {
            for (int j = 0; j < height; j++)
            {
                perlinNoise[i][j] += totalNoise[o][i][j] * amplitude;
            }
        }
    }
    for (int i = 0; i < width; i++)
    {
        for (int j = 0; j < height; j++)
        {
            perlinNoise[i][j] /= totalAmplitude;
        }
    }
    return perlinNoise;
}

I think it would also be worth mentioning that I have asked about this on StackOverflow as well.

\$\endgroup\$
5
  • 1
    \$\begingroup\$ Just so you know, it will never INFINITELY tile, but you could make it pretty large (like, the range of a 32 or 64 bit floating point number) \$\endgroup\$
    – Alan Wolfe
    Jul 22, 2015 at 1:35
  • \$\begingroup\$ You might want to check out amortized noise instead of perlin, it looks like it has some nice benefits for a procedural world - like random access reads. jcgt.org/published/0003/02/02 \$\endgroup\$
    – Alan Wolfe
    Jul 22, 2015 at 1:49
  • 1
    \$\begingroup\$ I don't have a comprehensive answer for you but here's a link to a webgl pixel shader that generates a seamless mountain range. You will likely find useful info in it! shadertoy.com/view/MdX3Rr \$\endgroup\$
    – Alan Wolfe
    Jul 22, 2015 at 1:51
  • 1
    \$\begingroup\$ Lastly, here's a link to an explanation of "Wang tiling" which is a really great technique for making things out of tiles where the end result doesn't look tiled at all. blog.demofox.org/2014/08/13/wang-tiling \$\endgroup\$
    – Alan Wolfe
    Jul 22, 2015 at 1:53
  • \$\begingroup\$ @Alan Wolfe I eventually plan on having a spherical world, so technically it doesnt need to be infinite, because you would eventually end up where you started if you went in a straight line for a long time. But I shouldn't run before I can walk, I need to get perlin noise working first. Also FYI, I plan on doing this by mapping the vertices of a cube to a sphere, where each face of the cube is a large grid of chunks. \$\endgroup\$
    – Kelan
    Jul 22, 2015 at 12:18

2 Answers 2

5
\$\begingroup\$

Given credit to Alan Wolfe for what he said on "INFINITELY tile". A 2d perlin noise (or a 2d simple noise) will have no seam problem as far as you stay away from noise borders (defined by floatin point dimenision) Referencing the image: enter image description here

and said that you have chunks with 128X128 vertex, in chunk i,j you compute each vertex as :

for x : 0 .. 128-1
 for y : 0 .. 128-1
  PerlinNoise2d.getValue(i*128 + x, j*128 + y)

this , will grant seamless between chunks.

If you want to simulate a infinite world regardless of noise borders , you may use a 3d noise and consider the values on the surface of a sphere.. but this is another story.

\$\endgroup\$
4
  • \$\begingroup\$ Hmm... well, this is very useful, but the fact that my noise is value noise rather than perlin noise is an issue. I used this to implement it, and the article is unfortunately named perlin noise but I later found out it is actually value noise. I cant find any useful tutorial on a perlin noise implementation in java at all. \$\endgroup\$
    – Kelan
    Jul 22, 2015 at 11:57
  • \$\begingroup\$ Another thing that has just occured to me is would this work for chunks at negative grid-coords? your diagram starts at 0,0 but could it start at Float.MIN_VALUE or Double.MIN_VALUE ? Would this have an effect on the noise generated with negative coords? This question is probably stupid, but comes from my lack of understanding of perlin noise. \$\endgroup\$
    – Kelan
    Jul 22, 2015 at 12:28
  • \$\begingroup\$ @NervezXx Note that in Java, MIN_VALUE is the smallest possible positive non-zero value, not the largest negative value. \$\endgroup\$ Jul 22, 2015 at 14:03
  • \$\begingroup\$ Oh, I never knew this. I know that Integer.MIN_VALUE is a really large negative number, so I thought it was the same for Double and Float.MIN_VALUE \$\endgroup\$
    – Kelan
    Jul 22, 2015 at 14:18
0
\$\begingroup\$

I know this is super old, but the underlying implementations of both perlin noise and value noise are just grids that interpolate value fields between cells, so there are two possible options for fixes.

Given a system where you are trying to tile arbitrarily seeded Perlin maps, keep track of the values at the edge nodes for each octave of seeding and run an interpolation pass on a cell the size of the largest octave using the values at the edges of the two divergent maps (for all the smaller octaves excepting the largest you will have to generate all the intermediary values aside from the edge node values)

The alternative system is one where the address of the node informs the seeding (for example, when calculating my random numbers to populate the values for the node, I seed the random generator with something like NodeSeed = GlobalSeed + x + ", " + y In that case every node given the same x, and y will produce the same random value every time. With such a system I can calculate the interpolation overlap with my neighboring node given infinite possible values of x and y. So when I do my interpolation pass I calculate one row/column past the actual value I need for every octave and use that to calculate the interpolated values that will be accurate to the edges of the next cell when I get around to generating it.

\$\endgroup\$

You must log in to answer this question.

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