Heightmaps are described, formally, as:
“ a raster image used mainly as Discrete Global Grid in secondary elevation modeling. Each pixel store values, such as surface elevation data, for display in 3D computer graphics.”
In summary, a heightmap is a map of values(often ranging from 0–1) which detail information such as elevation. Lower values relate to lower elevation, higher values relate to a higher elevation. Most heightmaps, whilst still in the format of lower and higher values, are represented in the form of greyscale imagery with lower values being represented as darker colours and higher values being represented as lighter colours.
Heightmaps, in one form or another, are a very popular way of generating terrain, be it 2D or 3D. As you can see from the example above, the lighter parts of the image could easily be mapped as steeper land, with the darker parts descending into water.
In combination with the heightmaps, which are ultimately just a means of holding and displaying information, we need a means to generate values for our heightmaps to hold. To generate these values, we utilise noise functions, specifically Simplex Noise in this example.
Simplex Noise, designed by Ken Perlin in 2001 to address the limitations of his classic noise function Perlin Noise, is a recent and widely accepted function used in all kinds of generation. The basic premise of both Simplex Noise and Perlin Noise is to combine multiple octaves of noise with differing frequencies and amplitudes to form a more natural and varied noise. The higher the octave count, the bigger each island will ultimately be.
Effectively, we are able to take coordinates(be them in 2D, 3D, 4D and so on..), supply them to the Simplex Noise function with a seed and get a 0–1 value we can use to create heightmaps.
We don’t need to know all the technicalities of how the Simplex Noise function generates the value when given coordinates and whilst the technicalities are beyond the scope of this article, you may find it interesting to look further into it.
We don’t need to know all the inner workings of Simplex Noise in order to use it(thankfully!), all we need to know is that it can take a coordinate and return a normalised value for use in whatever way we see fit.
For those of you interested in learning more about how Simplex Noise works, there’s an excellent paper on the topic of demystifying Simplex Noise by Stefan Gustavson of Linköping University — http://staffwww.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf
With those prerequisites out the way, we can get into the generation of the islands!
Firstly, we need to generate a heightmap with Simplex Noise. Our map is going to have a size of 2048 x 2048 pixels. In order to determine the number of octaves to sum, a decent approach often used is to find what power of 2 our resolution is. This number provides a scalable map with decently sized islands. 2048 as a power of 2 is 2¹¹ and thus we will sum 11 octaves.
Secondly, in order to vary our colouring later on we need to generate an additional heightmap of the exact same size — and thus the number of octaves, but with a different seed. This heightmap will be labelled as moisture and can be later used to determine biome placement.
Now we have these two heightmaps, we can apply colouring based on the combined elevation and moisture levels. For example, any value on the island map above a certain elevation — a certain brightness — will be rendered as grassland, the shade of which we can determine by cross-referencing the moisture levels in the same area. This method of colouring by elevation and moisture is applied to each element, sand, stone, snow and so on.
These look pretty good, but they spill out into the borders of the image which leaves the map feeling incomplete. For us to generate more whole, self contained islands, we need to degrade the borders of the image.
In order to factor out the outermost edges of these maps, we generate a square gradient with darker values at the center, lightening as it reaches the edges.
We can now take this square gradient and subtract it from our island heightmap, islands closer to the center will have less(or nothing) removed in comparison to the bordering islands, leaving us with the primary center islands and no split islands spilling off the image.
We can now take this altered heightmap and colour it just as we did before. Here’s the final product.
Just for fun we can also render the subtracted borders of our islands too, coloured and in heightmap form.
Thank you for reading :)