When I was young and learning to program, I was fascinated by the possibility of creating things that could live inside my monitor. I had the same feeling when I started to play with procedural content generation, which is to find the rules behind a phenomenon, encode them in an algorithm, and use that algorithm to create something virtual, but realistic — a plausible simulation.

Typically, you can give a seed or some initial parameters to a procedural content generation algorithm, and get some result. You could generate the landscape of a city, the shape of a tree or an entire world.

Procedural content generation has been in use for the last 40 years in video games, and it’s a key characteristic of rogue-like games. Instead of manually writing different levels, defining where walls are or where monsters are hidden, you could use an algorithm to generate that for you. You would need some intelligence in the algorithm so that all rooms are reachable, or that monsters are properly distributed across the dungeon. Fun fact: one of the initial reasons to use procedural content generation was to save memory (think about Daggerfall with its 161,600 square kilometers open terrain to explore). If you think about it, you can just save a seed and simply regenerate the world from it each time you play.

Procedural content generation can also be used to simulate scenarios, such as testing a robot’s control algorithm1. If you're able to encode the rules of a phenomenon, you can tweak some parameters and run a simulation to understand what factors are the most important in a scenario. Another example would be to simulate flooding by using government-provided digital elevation maps (DEMs) and adjusting various factors like precipitation, ground saturation and density. This would be of benefit to risk assessment in flood insurance or government risk-management analyses.

Nowadays procedural content generation is also used in movies when there is the need to generate many similar objects: think about a warehouse with thousands of nearly identical items, or an army of soldiers running down a hill. From a few samples and an algorithm introducing realistic differences you could generate a much larger sample which seems realistic to the eyes of the viewer.

While there are many different situations in which it's possible to use procedural content generation, it's more evident in well-known video games. Think of Diablo or Minecraft. You could play Diablo over and over and find new artifacts and different levels. Today you could walk in Minecraft until the end of time and keep seeing new land being generated for you.

There is one example which is less well known but highly respected by passionate geeks: Dwarf Fortress. In this game an entire world is simulated with its history. You could meet a character and find out that a certain ancestor of his slayed a monster who was haunting the area you're passing by. It is an intriguing mix of ASCII art graphics and incredibly deep simulation.

Procedural content generation can generate all sorts of things. You can be very ambitious and generate an entire world: this is the goal of WorldEngine7. In the rest of this article we'll take a look at how WorldEngine works.

There are many ways to generate a world, some of which are very simple. You can use simplex noise (a technique to generate random matrices of values between -1 and 1) to generate an elevation map, where brighter color means higher elevation:

However, this is not realistic. If you look at mountains ranges on Earth you will see that many of them tend to follow a line. Why is that? Because they appear at the border of tectonic plates. It is difficult to obtain realistic maps without simulating such phenomena. Indeed, the best way to generate a realistic world is to simulate the different physical phenomena which shape our world and make it appear as it is.

Simulating plate tectonics allows us to generate a world which has a realistic general appearance. That alone puts WorldEngine ahead of its open source competitors. But there are many other aspects to consider.

Another extremely important characteristic is erosion. There are several types of erosion, but the most significant one is hydraulic erosion: erosion caused by water.

16Map representing part of the Alps: you can easily see the channels produced by water flowing from the mountains. (View large version17)

If you do not simulate erosion you will never obtain realistic maps.

There are several algorithms to simulate hydraulic erosion and different aspects should be considered. In particular you want to pick an algorithm which has acceptable performance for your use case. If you want to explore this topic I suggest taking a look at “Fast Hydraulic Erosion Simulation and Visualization on GPU”18 by Xing Mei et al.

You need to consider this point: where does the water come from? From precipitation and ice melting on the top of the mountains.

Consider the factors which control precipitation: the latitude, the temperature, winds, the rain-shadow effect, and so on. The list of phenomena is never-ending. Of course, in a simulation your goal should not be to reproduce completely the complexity of the real phenomena, but understand which elements are the most relevant and how to represent them, perhaps approximately.

There are many aspects to consider and all of them are very interesting to learn, but if your implementation time and CPU cycles are limited you need to identify your priorities. In WorldEngine we spent a good amount of energy refining our biome simulation, which is based on Holdridge life zones19.

While other models could be slightly more precise, they require the simulation of seasonality, which is something we chose not to do in WorldEngine. We have a model which considers temperature and humidity, and can determine what kind of biome is present in an area among forty different types: from subpolar dry tundra to tropical rain forest, passing by subtropical desert scrub. Sure, not all of our users need such a complete model, and these biomes can be grouped in families like desert or jungle.

This article is an introduction to procedural content generation so it doesn’t make sense to go into too many technical details, but it is important to give an idea of how this stuff can be implemented.

Procedural content generation can be used to simulate very different things, so most algorithms are specific to single applications. However, many different applications of procedural content generation use some form of noise. This is the case because reality is incredibly complex and rich with details. To properly simulate most real-life phenomena would require unfathomable computational power. Think about a world generator: while it makes sense to represent the phenomena which shape the continents and the main mountain ranges, we cannot simulate single rocks and determine the precise form of the shorelines. What we can do is add some noise to the high-level simulations, obtaining something which is still realistic and detailed, but doesn’t require hundreds of thousands of years to be computed. This sounds like a bargain to me!

There are several noise functions. Perlin noise was very widespread in the past, while today it has been superseded by simplex noise. You can think of it as a function taking several parameters and returning a value between -1 and 1. Those parameters represent several dimensions: if you fix all but two of them you will get a bidimensional function which represents your bidimensional noise. You can regard the parameters you fixed as your seed.

Now, a great thing you can do with noise is to compose noise using different frequencies and amplitudes. Suppose you have a map and you’ve applied noise to it. Then you add a new simplex noise (changing the seed) but this time you divide the value of the noise by two, and you also increase the frequency (you simply multiply your coordinates by two). In this way, the second octave of noise will vary twice as fast but will contribute a smaller noise. You can repeat this many times, obtaining a complex noise, which shows both some general pattern and fine-grained details.

20In this series of images you can take a look at how composing noise works. Each of the first seven images has a frequency double of the one preceding it. The last image combines all the images with different factors (the first image has the strongest factor, the second has half that factor, and so on). The final result presents the general pattern of the first image but it has many more details. (View large version21)

Time to see some code. Let’s see how we can generate those images in Python:

If you look at the main function, you can see that we generate different noise with an increasingly smaller period. A smaller period corresponds to a higher frequency. It means that the noise becomes faster – it varies more in a shorter space. We then combine all the single noises with a weight which ranged from 1 for the noise with the shortest period, to 64 for the ones with the longest period.

We use the p-opt.png library to generate images. We simply associate each point with a color ranging from black to white. Black will correspond to the lowest possible value (-1.0), and white to the highest one (+1.0).

At this point we could generate a proper map from this combined noise. The idea is simple: points with high values will be mountains and points with low values will be ocean. In WorldEngine we calculate which level corresponds to sea level by looking for the value which produces a certain percentage of land (for example, 70%). To keep things simple we just pick an arbitrary value (0.25, see line 1 in the code below).

We assign specific colors to certain elevation values. We have one for: the deepest possible sea (elevation -1.0); for the shallowest sea (elevation just below 0.25); for the coast (elevation 0.25); for hills (elevation 0.65); and for mountains (elevation 1.00). Then we look at the elevation of each point and find the range it is contained in.

We can then calculate the color of that point as a mix between the colors of the two extremes of the range. For example, a value of elevation 0.70 will correspond to a color obtained by mixing the color for hills and the one for mountains. It will be closer to the color for hills because 0.70 is closer to 0.65 (hills) than 1.00 (mountains). You can see the result in the image below.

To add more character to the map we can apply erosion. There are several algorithms to implement erosion and some of them work better at a low-level scale. However, here we have the map of an entire world and we need to use an algorithm which works at an appropriate scale, producing results which are realistic, visible and can be run in a reasonable amount of time. We are going to adapt the algorithm we use in WorldEngine.

The algorithm is designed to work on an instance of world, a class defined in WorldEngine. For this example, we provided a simple implementation of world which can be instantiated passing the elevation map we have created together. A world should also have its precipitation map, but in this case we just assume precipitation is uniform and we pick an arbitrary value. We need also an implementation of the A-Star algorithm to find paths. We added that implementation in the example available at GitHub24 but unfortunately there’s no space to discuss it here. Let’s take a look instead at the interesting part: the erosion algorithm:

We start by calculating for each cell, which direction the water would flow from there (line 10), then we find river sources. We look for points with high elevation which collect a certain amount of in-flow from other cells (line 13). Then we calculate the path between the sources and the sea. Rivers which cannot reach the sea generate lakes (line 23). At this point we calculate erosion (line 27). Basically, we remove terrain depending on the amount of water flowing in. At the end of this process we have an eroded map, and as a by-product we have also a list of rivers and lakes present in our world.

25Result of an eroded map. (View large version26)27If you observe the map closely you’ll notice the effects of erosion. Adding shadows makes it much easier to perceive the depth of the map and to pick out valleys and ridges carved by hydraulic erosion. (View large version28)

Drawing the shadows is reasonably easy. You suppose the sun has a certain position with respect to the map (for example, north-west); then for each cell you consider a few neighbours which are between the sun and the cell in question. If they have a greater altitude than the cell, they will cast a shadow on it. The color of the cell is than mixed with black (using the gradient function) proportionally to the amount of shadow present. More shadow means that the cell will look darker.

In WorldEngine we use a lot of different algorithms for our simulations, but we apply simplex noise for all of the results. We start by using noise to generate the initial height map. Then we run the plate simulation: we move the plates around, we simulate plates clashing, mountains rising and so forth. To the final results we add more noise. At this point we translate the map to place the lowest elevation at the borders and the highest at the center. We then start to pour water from the borders, until we cover a certain percentage of the map. This is how we get the preliminary height map.

We then calculate the temperature. This depends on the elevation and the latitude of each single point. After that, we calculate the precipitation, which depends on the temperature and the noise. Once we have the temperature and the precipitation we are able to simulate hydraulic erosion. It is not really easy to get something which is realistic and fast enough to calculate. Our algorithm selects initial flow sources and then simulates how the water moves down, removing terrain as it passes and bringing sediment with the flow. It then looks for a path to the sea. It could end in a pit and form a lake, or manage to grow into a river. This is how we get our refined height map.

We consider precipitation for each single cell and we look how the water flows. Obviously, not all the cells will produce the same amount of precipitation. Because of the erosion, normally paths will tend to converge and water will be grouped along rivers. We determine in this way the single streams and the amount of water for each of them. Once we have the streams we calculate which areas can be irrigated from these streams and the permeability and humidity of each zone; once again we use noise in those steps. Finally we calculate the icecaps and we are done.

As you can see, we use many different simulations (and we are going to add some more), but one fundamental element is noise: without it, all simulations would seem predictable and boring.

Once you have run several simulations you have a complete world full of details. You could now use it for your games, your simulations or any other goal that comes to mind. However, you have a new problem: how can you take a look? How do you represent this complex world for the user?

For each single point you have several pieces of information: you know the temperature and the precipitation, the elevation and the biome, even on which tectonic plate it sits.

One possible use is to generate beautiful maps to use as a wallpaper. WorldEngine can generate worlds in different open formats (currently protobuf and HDF5) for its users to do whatever they want.There are libraries which can load these formats in many different languages (for example, you can load protobuf files in C++, Java, Python, Ruby, Nano, Objective-C and C#).

The creator of the Air XenoDawn video game told me that starting from a map helped him to create a story and give it some depth. I read a similar sentiment in the Rivan Codex from David Eddings46: in that book my favorite fantasy novelist describes how he created his saga and how for him a good map was central to the creativity process. From a map he could envision the specificity of the people who lived there, by the coast or on the mountains, in a cold region or close to equator.

To generate realistic worlds through procedural content generation is not an easy task. In addition to a theoretical background, a lot of effort is needed to implement different simulation techniques in way that is realistic and practical: no one wants to wait ten hours in front a monitor to generate a world.

To build such a complex world generator has been possible because a community has grown around it. We have taken advantage of an existing project named PlaTec, built by Lauri Viitanen50 for his Masters thesis, and implemented the plate tectonics simulation. The community resurrected his code, porting it on several platforms, evolving it and make it more easily usable.

WorldEngine was then born by merging two separate projects, created by the current maintainers of WorldEngine (me and Bret Curtis).

To make WorldEngine a healthy open source project, a lot of effort has been made on improving tests, continuous integration, support for several platforms, building binary packages, addressing the issues reported, and promoting the project. From these efforts a healthy community of about ten developers has been grown, and more and more features are being implemented. Recently version 0.19.0 was released and the work keeps going on toward version 1.0.0. We hope to reach that soon and keep working to provide better worlds where endless adventures can be had. We hope you can have fun with WorldEngine and perhaps help us improve it.

Procedural content generation is fascinating: it helps us understand the forces which shape reality and allows us to play with them, creating something beautiful. I find it fulfilling just to learn about the phenomena which shape the world and simulate them, but this is just the start of infinite possible adventures that can be set on these worlds: all sort of games and simulations can use the results of procedural content generation in general and WorldEngine in particular. Infinite, complex, realistic worlds are just one click away.

Federico Tomassetti got his PhD in Software Enginering and worked in
Academy and industry in Italy, Germany, Ireland and France. Among
others he worked for TripAdvisor and Groupon. You can read more about
his work at tomassetti.me

Bret Curtis holds a Bachelors in Computer Science and has worked as a
Software and Systems Engineer who loves startups and FOSS. Bret has
been involved in the Open Source world since 1996 and has a passion
from cross-platform compatibility You can read more about his work at
http://www.mindwerks.net

Adam Goodrich

If you are doing procedural generation on Unity3D then you might be interested in Gaia. Gaia allows you to design the environments that suit your game, and then procedurally textures, plants and populates the rest of your scene. Saves you a ton of time, and gives your game a pretty starting point. Gaia also has a very active and helpful community in its forums. NOTE: I am the author of Gaia and hope that this post is considered as interesting and relevant rather than spam. http://u3d.as/ijC

0

3

Federico Tomassetti

Kumsal Obuz

I think the code needs serious fixes here and there with indentation and especially with multiple return statements in certain functions such as elev_to_pixel and calc_elev. Also, for the love of me, what’s p-opt.png? I can’t find a trace of it let alone install it. All in all, I enjoyed this article and there is so much potential to use the put in, for example, Unity to build a terrain for your game.

webdevelopemnt

Federico Tomassetti

Michał Sadowski

Great article! I’ve been interested for quite a long time in procedural world generators and found them rather lacking. Have you ever seen a method of creating procedural worlds not by tiles, but rather in vector formats?

0

9

Federico Tomassetti

This is something I considered for a long time. However I think that it would be quite complex from the point of view of performance. We could use noise that scale to an infinite level of details but we would have problem with things like the plate tectonics simulation. Aside from the performance point of view we are doing a lot of different simulations which would be simply much more difficult to code using a vectorial approach, I think.

Today, too many websites are still inaccessible. In our new book Inclusive Design Patterns, we explore how to craft flexible front-end design patterns and make future-proof and accessible interfaces without extra effort. Hardcover, 312 pages. Get the book →

Meet the new Sketch Handbook, our brand new Smashing book that will help you master all the tricky, advanced facets of Sketch. Filled with practical examples and tutorials in 12 chapters, the book will help you become more proficient in your work. Get the book.

Meet SmashingConf San Francisco 2017, featuring front-end ingredients, UX recipes and nothing but practical beats from the hidden corners of the web. Only practical, real-life techniques and recipes you can learn from. Get your ticket now!