Home About Recent Martial Responsibility For Safety The Buffalo Trap Longsword Solo Drills - Meÿer Square Starting Historical European Martial Arts Group Historical European Martial Arts - Equipment Game development Generic A* for Games Procedural Island Generation Data-Oriented Design Matters Misconceptions of Component-Based Entity Systems Level of Detail Experiments Planet Generation - Part II Planet Generation - Part I Procedural Generation in Games Oculus Rift Integration Android Favorite Android Games NDK with Android Studio Android NDK Programming - Part III Android NDK Programming - Part II Android NDK Programming - Part I Personal Personal Stuff: Running! Global Game Jam 2014 Experiences Anime Claymore The Twelve Kingdoms Games Favorite Android Games Dungeons & dragons D&D Journal: I Historical european martial arts Martial Responsibility For Safety The Buffalo Trap Longsword Solo Drills - Meÿer Square Starting Historical European Martial Arts Group Historical European Martial Arts - Equipment Longsword Martial Responsibility For Safety The Buffalo Trap Longsword Solo Drills - Meÿer Square

In the previous post I explained how to create the sphere geometry for the planet.

In this post I will explain how to add height data to the vertices which will create continents, islands and oceans.

The plan

The plan for this post is the create height data for each face of the sphere, displacing the positions to create land and oceans.

One important quality is that generation should be pseudorandom, allowing the creation of many different looking planets and also completely replicating the same generation by suppliying a unique seed to the random generator.

Existing Geometry

The current geometry consists of 6 grids collapsed into a sphere. This fact simplifies a lot of the work we have to do. If one wants to create a texture they can easily retain the UV mapping of a grid and attach a texture for each face or just use a cube map. Earth mapped to a cube

For future level of detail reasons I decided to split my geometry into 6 different vertex buffers, which allows me to completely cull invisible individual planes and implement LOD algorithms to each plane as if it was an isolated terrain chunk.

for ( int j = 0; j < numRows; ++j )
{
    for ( int i = 0; i < numCols; ++i )
    {
        vertices[  j * width + i ].UV.x = (float)i / (float)(numCols - 1);
        vertices[  j * width + i ].UV.y = (float)j / (float)(numRows - 1);
    }
}

Now that we have UV mapping, color and height textures can be applied.

Generating height maps

As I mentioned in the previous post, part of the reason I chose procedural content generation is to compensate for my lack of artistic skill. In other words the best way for me to create textures to apply to the planet is by creating a program that will generate them for me. And they said programmers are safe from AIs taking our jobs...

We have 6 grids each in need of height data, one special requirement is that the data will wrap back nicely along the edges of each face of the grid. Past experience with content generation had me Google for perlin noise terrain generation keywords which lead to this tutorial.

Libnoise is a portable, open-source, coherent noise-generating library for C++. This sounds good for my needs as I am running the code on my Nexus 5 Android phone and the library supports a SetSeed function so it fills the requirement of being able to replicate the same results given a seed.

The generated maps can be easily cut into a cube map or individual textures for each face of the sphere. This is an example of a generated map from the tutorial link above:

sphericalheightmap.jpg

It uses Perlin noise in its tutorial on terrain generation. Perlin noise is a gradient noise generator known to be a good for generating fractals and is used to simulate elements from nature like clouds, smoke and terrain.

Perlin.png

Unfortunately, after experimenting with it for a while I felt it is too slow to generate the data and I looked for alternative. This let me to Simplex Noise which is faster and delivers similar results to Perlin noise. It can also run on the GPU.

Using Simplex Noise

Now that I decided on a noise algorithm I needed an implementation that will satisfy our constraints. I found this implementation as a lightweight C++ solution and started to experiment for results.

The useful function in the simplexnoise.h file for our case is the scaled_octave_noise_3d which allows noise to be generated for 3D coordinates, satisfying our need for wrapped the textures along the intersections of the planes.

float scaled_octave_noise_3d( const float octaves,
                            const float persistence,
                            const float scale,
                            const float loBound,
                            const float hiBound,
                            const float x,
                            const float y,
                            const float z );

For each vertex after mapping the position to a sphere, I applied displacement using this function:

void CalculateHeight( Vector3& vPosition )
{
    // Get the direction vector from the center of the sphere
    Vector3 vNormalFromCenter = vPosition;
    vNormalFromCenter.Normalize();

    // Variables for the noise
    static const float HEIGHT_MAX = 24.5f; // Planet Radius is 1,000.
    static const float HEIGHT_MIN = -31.0f;
    static const float NOISE_PERSISTENCE = 0.6f;
    static const float NOISE_OCTAVES = 8.0f;
    static const float NOISE_SCALE = 1.0f;

    // Generate the noise for the position
    float fNoise = scaled_octave_noise_3d( HEIGHT_OCTAVES, HEIGHT_PERSISTENCE, NOISE_SCALE, HEIGHT_MIN, HEIGHT_MAX, vPosition.x, vPosition.y, vPosition.z );

    // Keep ocean level as base level
    if ( fNoise <= -0.0f )
        fNoise = 0.0f;

    // Displace the position
    vPosition += vNormalFromCenter * fNoise;
}

This led to the result shown in the first image on this page. I applied RGB(28, 107, 160) for all heights at 0.0f or below and RGB(61, 182, 29) for all land masses.

Note the values for minimum and maximum height. As the earth is about one third land and two thirds water I decided to experiment with values that will produce around 33%-40% land. After experimenting with the values I found -31.0 to 24.5 produced nice results. Feel free to experiment. Also, my planet has a radius of 1,000 so these values made somewhat sense on a relative scale.

These are quite satisfying as a fast solution to begin with. In the future I hope to increase the detail by applying a second noise map with less persistence and/or increased scale on heights above ocean level. This should generate additional mountains and less consistent land masses.

Here is an example of a planet with NOISE_SCALE = 2.5f:

Applying a seed

As I mentioned at the beginning of the post, one of the requirements for the generator is to be able to replicate a result given a seed. While libnoise library offered a SetSeed function, SimplexNoise had no implemented method to use.

Luckily implementing one was quite easy. In the simplexnoise.h header there is an array of numbers used as a permutation table.

static const int perm[512] = { ... }

The array basically contains all numbers from 0 to 255 shuffled randomaly. The sequence is then duplicated once to fill the array.

By changing the array from a const and applying the following function you can easily set the seed and replicate the same results for a planet:

// Variables
// perm - SimplexNoise's Permutation table
void set_seed( unsigned int seed )
{
    // Set the seed for random generator
    srand( seed );

    // Set up the random numbers table
    for ( int i = 0; i < 256; ++i )
    {
        // Put each number in once
        perm[ i ] = i;
    }

    // Randomize the random numbers table
    for ( int i = 0; i < 256; ++i )
    {
        // Replace current value with random value from the table
        int k = perm[ i ]; // current value

        // Random table cell
        int j = (int)random( 256 );

        // Replace two values
        perm[ i ] = perm[ j ];
        perm[ j ] = k;

        // The table repeats itself so just assign repeating values the same way
        perm[ 256 + i ] = perm[ j ];
        perm[ 256 + j ] = k;
    }
}

Next

This is all for this post. In following posts I will look into lighting, Atmospheric Scattering, generating of biomes and colors for the planets and level of detail.

Back
Home About Recent Martial Responsibility For Safety The Buffalo Trap Longsword Solo Drills - Meÿer Square Starting Historical European Martial Arts Group Historical European Martial Arts - Equipment Game development Generic A* for Games Procedural Island Generation Data-Oriented Design Matters Misconceptions of Component-Based Entity Systems Level of Detail Experiments Planet Generation - Part II Planet Generation - Part I Procedural Generation in Games Oculus Rift Integration Android Favorite Android Games NDK with Android Studio Android NDK Programming - Part III Android NDK Programming - Part II Android NDK Programming - Part I Personal Personal Stuff: Running! Global Game Jam 2014 Experiences Anime Claymore The Twelve Kingdoms Games Favorite Android Games Dungeons & dragons D&D Journal: I Historical european martial arts Martial Responsibility For Safety The Buffalo Trap Longsword Solo Drills - Meÿer Square Starting Historical European Martial Arts Group Historical European Martial Arts - Equipment Longsword Martial Responsibility For Safety The Buffalo Trap Longsword Solo Drills - Meÿer Square