programming, worldbuilding

Mapping Planets

Distortions, Seamless Noise, and how to handle it.

Generating the planet

One of the simplest ways to generate a planet, is to use noise, which gives you a couple of options on which noise to use, let’s inspect each one and determine the best option:

  • Perlin Noise – Perlin Noise is the most basic option, it was developed by Ken Perlin in 1983, however it has a couple shortcomings, there are a couple visual artifacts and it’s speed at generating large images can be quite slow when compared to the other alternatives.
  • Simplex Noise – Simplex Noise was designed by Ken Perlin in 2001 in an attempt to address Perlin Noise’s shortcomings, it’s a pretty decent and fast solution however it has a major drawback, the use of 3D Simplex Noise is protected under a patent, which makes using it a pretty expensive affair.
  • Open Simplex Noise – Open Simplex Noise was developed by KDotJPG with one simple goal in mind: make a modern alternative to Simplex Noise that is free to use, that has no distortions and is relatively fast.

Out of the three I personally prefer Open Simplex Noise and is what I use for my personal projects. Do take note, the current implementation of OpenSimplexNoise needs some extra work so you can get easy access to the scale, octaves, and seeds. There is a multitude of guides out there on the internet explaining what each of these does and I highly recommend a read, however, that is not what I want to talk about in this blog post.

This is what Open Simplex Noise with 16 octaves looks like.

Seamless noise

Noise is infinite, which means if we simply give it a canvas with a ratio of 2:1 to make an equirectangular map it will not loop when you eventually try to put the map on a sphere(shoutout to this amazing website), and you will get massive variations on the horizontal seam and on the poles.

There is a multitude of ways to fix this, on this amazing blog post Red Blob Games only needs to generate an island, using a function that takes distance from the center as a variable and setting it height 0 at the edges minimizing seams.

However, this is not what we want. We want to generate a planet, with the possibility for north and south poles and to do that we are gonna need some more complex math calculations.

Spherical Mapping

A method that can generate spherical planets would be to convert the cartesian coordinates of our canvas to spherical coordinates, generate noise based on those coordinates and finally convert that noise back to cartesian and map it to the canvas.

However this implementation has its limitations, and the why and how it happens is demonstrated by Ron Valstar in this brilliant blog post mainly the fact that the shapes of the continents look extremely weird and distorted and therefore is not what I want.

Cube Mapping

The second method I ended up using was motivated by both Ron Valstar’s post and acko’s Making Worlds series of blog posts, in them, they describe the generation of a globe through generating a cube and then inflating it as if it was a balloon until it’s shaped like a sphere.

All credits to acko.net for this image, it explains the cube map concept in a easy way to visualize.

Now we simply have to generate six faces which is easy enough, there is a multitude of ways to do it.

The way I ended up doing it was by creating an array and then populating it with data. Converting the 2D coordinates of a canvas to the 3D coordinates of a cube and then generating noise for each of these 3D coordinates to then store them in the corresponding 2D coordinate value.

//Z STATIC
for(int y = 0; y < cubeFaceSize; y++) {
	for(int x = 0; x < cubeFaceSize * 2; x++) {
		//Generates FRONT
		if(x < cubeFaceSize) {
			cubeMap[cubeFaceSize+x][cubeFaceSize+y] = noise.noise3D(x, y, 0);                    
		}
		//Generates BACK
		else {
			cubeMap[cubeFaceSize*3+(x-cubeFaceSize)][cubeFaceSize+y] = noise.noise3D(cubeFaceSize-(x-cubeFaceSize), y, cubeFaceSize);
		}
	}
}
//X STATIC
for(int y = 0; y < cubeFaceSize; y++) {
	for(int x = 0; x < cubeFaceSize * 2; x++) {
		//Generates LEFT
		if(x < cubeFaceSize) {
			cubeMap[x][cubeFaceSize+y] = noise.noise3D(0, y, cubeFaceSize-x);                   
		}
		//Generates RIGHT
		else {
			cubeMap[cubeFaceSize*2+(x-cubeFaceSize)][cubeFaceSize+y] = noise.noise3D(cubeFaceSize, y, x-cubeFaceSize);
		}
	}
}
//Y STATIC
for(int y = 0; y < cubeFaceSize * 2; y++) {
	for(int x = 0; x < cubeFaceSize; x++) {
		//Generates TOP
		if(y < cubeFaceSize) {
			cubeMap[cubeFaceSize+x][y] = noise.noise3D(x, 0, cubeFaceSize-y);          
		}
		//Generates BOTTOM
		else {
			cubeMap[cubeFaceSize+x][cubeFaceSize*2+(y-cubeFaceSize)] = noise.noise3D(x, cubeFaceSize, y-cubeFaceSize);
		}                
	}
}

This way, we can generate a cube map, which is easily converted to an equirectangular map thanks to the amazing code snippet provided by Bartosz.

As you can see, the equirectangular map has much better-looking shapes, and when wrapped around a sphere it produces similar results to Spherical mapping without all its downsides, besides, an equirectangular projection can be easily converted by multiple programs, such as NASA’s G.Projector to basically any kind of map that has ever existed.

Closing Thoughts

Generating an entire planet can be a daunting task, and although noise can be quite a powerful tool if used properly it is still bound to problems that humans have had for centuries, such as how to map a globe to a 2D canvas with minimal distortion.

The solution I propose here only produces very roughly generated planets, with no regards whatsoever towards tectonic plates, rivers, proper island chains, or even mountain ranges and therefore is only really meant to be used as a demonstration or starting basis for more advanced simulations.

In fact, it only really produces a matrix of values within a certain specified range, for the greyscale images they are within 0 to 255 and are then converted to a pixel producing an image similar to the first greyscale image or on a range from -11000 to 8000 emulating real-world height differences and then painting the same color for height intervals.(eg: between 0 to 5 paint it in a sandy color to emulate beaches)

God used beautiful mathematics in creating the world.

Paul Dirac