libnoise logo

A portable, open-source, coherent noise-generating library for C++


Tutorial 8: Creating spherical planetary terrain

In this tutorial, you'll generate a terrain height map that can be seamlessly wrapped around a sphere. These kinds of height maps are ideal for creating your own planets.

Configuring the spherical noise-map builder

In all of these tutorials so far, you've created a terrain height map using a planar noise-map builder. This builder generates its input values along the surface of a two-dimensional plane. Because libnoise works with three-dimensional input values, the noiseutils library provides some builders that generate these values along the surface of a specific three-dimensional object.

In this tutorial, you'll use a spherical noise-map builder. This type of builder generates its input values along the surface of a sphere. It describes these input values using a spherical coordinate system (latitude/longitude) instead of a planar coordinate system (x, z). It then "flattens" these coordinates onto a plane so that it can write coherent noise into a two-dimensional height map. This process is called spherical mapping.

To create a spherical noise-map builder, you'll replace the NoiseMapBuilderPlane object with a NoiseMapBuilderSphere object.

In preparation for this tutorial, open the source file you created in the previous tutorial and erase all the code in it. Add the following code:

#include <noise/noise.h>
#include "noiseutils.h"

using namespace noise;

int main (int argc, char** argv)
{
  module::Perlin myModule;

  utils::NoiseMap heightMap;

  utils::RendererImage renderer;
  utils::Image image;
  renderer.SetSourceNoiseMap (heightMap);
  renderer.SetDestImage (image);
  renderer.ClearGradient ();
  renderer.AddGradientPoint (-1.0000, utils::Color (  0,   0, 128, 255)); // deeps
  renderer.AddGradientPoint (-0.2500, utils::Color (  0,   0, 255, 255)); // shallow
  renderer.AddGradientPoint ( 0.0000, utils::Color (  0, 128, 255, 255)); // shore
  renderer.AddGradientPoint ( 0.0625, utils::Color (240, 240,  64, 255)); // sand
  renderer.AddGradientPoint ( 0.1250, utils::Color ( 32, 160,   0, 255)); // grass
  renderer.AddGradientPoint ( 0.3750, utils::Color (224, 224,   0, 255)); // dirt
  renderer.AddGradientPoint ( 0.7500, utils::Color (128, 128, 128, 255)); // rock
  renderer.AddGradientPoint ( 1.0000, utils::Color (255, 255, 255, 255)); // snow
  renderer.EnableLight ();
  renderer.SetLightContrast (3.0);
  renderer.SetLightBrightness (2.0);
  renderer.Render ();

  utils::WriterBMP writer;
  writer.SetSourceImage (image);
  writer.SetDestFilename ("tutorial.bmp");
  writer.WriteDestFile ();
  
  return 0;
}

At this point, this source file is similar to the file you created at the end of the third tutorial, but the NoiseMapBuilderPlane object has been removed.

Now, you'll create a NoiseMapBuilderSphere object and pass the Perlin-noise module to it. Add the following highlighted code:

  utils::NoiseMap heightMap;
  utils::NoiseMapBuilderSphere heightMapBuilder;
  heightMapBuilder.SetSourceModule (myModule);
  heightMapBuilder.SetDestNoiseMap (heightMap);

Next, you'll specify the size of the terrain height map to generate. Because you're generating a spherical height map, its width should be double its height in order to reduce warping. For this tutorial, you'll specify a size of 512 x 256 points. Add the following highlighted code:

  utils::NoiseMap heightMap;
  utils::NoiseMapBuilderSphere heightMapBuilder;
  heightMapBuilder.SetSourceModule (myModule);
  heightMapBuilder.SetDestNoiseMap (heightMap);
  heightMapBuilder.SetDestSize (512, 256);

Now, you'll need to set the (latitude, longitude) coordinates of the bounding rectangle in which to retrieve the coherent-noise values. Because you're generating a terrain height map for an entire planet, you'll set these coordinates such that it encompasses the entire planet. This bounding rectangle is bounded 90 degrees south, 90 degrees north, 180 degrees west, and 180 degrees east. Add the following highlighted code:

  utils::NoiseMap heightMap;
  utils::NoiseMapBuilderSphere heightMapBuilder;
  heightMapBuilder.SetSourceModule (myModule);
  heightMapBuilder.SetDestNoiseMap (heightMap);
  heightMapBuilder.SetDestSize (512, 256);
  heightMapBuilder.SetBounds (-90.0, 90.0, -180.0, 180.0);

Coordinates in the eastern and northern hemispheres are positive. Coordinates in the western and southern hemispheres are negative.

Now you can build the spherical height map. Add the following highlighted code:

  utils::NoiseMap heightMap;
  utils::NoiseMapBuilderSphere heightMapBuilder;
  heightMapBuilder.SetSourceModule (myModule);
  heightMapBuilder.SetDestNoiseMap (heightMap);
  heightMapBuilder.SetDestSize (512, 256);
  heightMapBuilder.SetBounds (-90.0, 90.0, -180.0, 180.0);
  heightMapBuilder.Build ();

Now you're ready to render the spherical height map. Compile and run the program, then open the tutorial.bmp file:

Rendering of a spherical terrain height map

This image has some interesting properties:

  • The left and right edges seamlessly join together. Open this image in your favorite image editor to see for yourself.
  • It becomes increasingly distorted with distance from the equator. Maximum distortion occurs at the poles. (These distortions disappear when you wrap it around a sphere.)

If you use a 3D application to wrap this image around a sphere, you'll create a planet that has no texture distortions nor seams. For example, this image has been applied to a planet in Celestia, a real-time visual space simulator:

Spherical terrain height map in Celestia

If you have Celestia and you feel comfortable with modifying the solarsys.ssc, you can wrap this image around a planet, and then spin the planet in any direction and not find any texture seams, other than some minor artifacting near the poles. (Modifying this file is beyond the scope of the tutorial.)

Zooming in

In this section, you'll zoom into a small area of the planet and render the terrain height map from that area. You'll zoom into an area defined by a bounding rectangle, 30-degrees on a side, with the following coordinates:

  • Southernmost coordinate: N 0°
  • Northernmost coordinate: N 30°
  • Westernmost coordinate: E 50°
  • Easternmost coordinate: E 80°

To zoom into that area, modify the following highlighted code:

  heightMapBuilder.SetDestNoiseMap (heightMap);
  heightMapBuilder.SetDestSize (512, 256);
  heightMapBuilder.SetBounds (0.0, 30.0, 50.0, 80.0);
  heightMapBuilder.Build ();

Because this bounding rectangle is actually a square, the terrain height map should also be square. Modify the following highlighted code:

  heightMapBuilder.SetDestNoiseMap (heightMap);
  heightMapBuilder.SetDestSize (256, 256);
  heightMapBuilder.SetBounds (0.0, 30.0, 50.0, 80.0);
  heightMapBuilder.Build ();

Once again, compile and run the program, then open the tutorial.bmp file:

Zoomed-in rendering of a spherical terrain height map

Unfortunately, the terrain height map lacks detail at this zoom level. This is because the number of octaves that is used to generate the Perlin noise is rather small. To add more detail, you'll need to increase the number of octaves. Modify the following highlighted code:

  module::Perlin myModule;
  myModule.SetOctaveCount (10);

Compile and run the program again, then open the tutorial.bmp file:

Detailed zoomed-in rendering of a spherical terrain height map

Notice that this rendering has more detail than the previous rendering.

Conclusion

In this tutorial, you've created a spherical height map that is suitable for generating entire planets. You've seen how renderings of these height maps can be seamlessly wrapped around spherical objects in 3D applications. You've also zoomed into a small area of the height map and increased its detail.

With all of the tutorials you've done, you have learned enough about libnoise to create immense worlds to explore. See what you can come up with.