Generating Better Terrain Heightmaps

 

Most tutorials dealing with PCG terrain in Unity often revolve around Perlin Noise and how it’s used. The basic implementation is pretty simple, just a single perlin noise call for each point on the terrain grid, and the results are serviceable for many cases. However, these types of terrain can often look smooth and pillow-like, not exactly natural. Attempts to increase “roughness” involve scaling the parameters of the noise function call to higher values, but often times this results in crowded, unnaturally thin peaks without excessive tweaking. In the last weeks of my AI class I and a team of 2 others developed a scriptable wizard in Unity that would allow us to generate terrain. My work primarily was on finding ways to generate realistic heightmaps.

A mountain and plain biome with 1 layer of perlin noise, generally makes for smooth but unnatural looking terrain

A mountain and plain biome with 1 layer of perlin noise, generally makes for smooth but unnatural looking terrain

Layered Perlin Noise

The simpler solution to get more realistic looking terrain without completely redoing the simple implementation listed above is to add layers of perlin noise. This is done by placing the noise call in a loop for a desired amount of iterations (layers), increasing the noise scale and multiplying the result by a decreasing amplitude each time, and summing the results in the end to get a new height for each point. This is somewhat simulating more octaves for the perlin function call, and depending on the amount of octaves/layers we can produce more realistic results than a single noise call.

The same biome setup but with 7 layers of perlin noise

The same biome setup but with 7 layers of perlin noise

Diamond Square Algorithm

A different approach I used was the Diamond-Square Algorithm which is also a method to generate realistic heightmaps or even procedural textures. The graph representation of how the algorithm works is rather simple. Starting with two points of a random height on a graph, we get the midpoint between them with a height that is the average of the other two. We then add a random roughness range onto the midpoint, and we recursively repeat these steps for every point on the graph until we reach a certain number of subdivisions.

Diamond-Square takes this to a grid that in our case functions as a height map. Starting with 4 seed points on the corners of a random height, we do the Diamond Step which calculates the midpoint with a random roughness addition like the process above from the 4 points. Next is the Square Step which instead of using points diagonal to the new midpoints, we use 4 points orthogonal to it. We then repeat these two steps gaining subdivisions each iteration until we’ve reached the subdivision maximum, which in this case is the unit size of the grid. The resulting points with their new heights become the heightmap.

Using diamond-square instead of perlin noise

Using diamond-square instead of perlin noise

My implementation had to differ slightly from other standard implementations of this algorithm, as most involved feeding the heightmap directly to a plane mesh using a with a 2D array size of some power of 2 incremented by 1, or 2n+ 1. My implementation just had to use the terrain object’s dimensions directly else array bounds would be an issue or the amount of subdivisions would be insufficient to finish generating the terrain. Feel free to check out the project page for a link to download the project and source code, and take a look at the research paper for an explanation of the other parts of the project.

[Research Paper]

[Project Page]