A procedural terrain generation demo made using Unreal Engine 5 and C++. This project was made for a procedural content generation assignment requiring the implementation of hydraulic erosion.
I decided to make this project in Unreal Engine, where multiple sections of a procedural mesh are generated using noise functions, then erosion is applied globally. I also included a landscape material with layers for grass, cliffs and beaches, as well as procedural foliage generation to enhance the realistic look.
The implementation process
I started with calculating how many sections are necessary and generating the height data for each using noise functions. Then, I updated the vertex and index buffer for each section on the procedural mesh. With the system I designed, all the calculations were running asynchronously with only the mesh updates having to take place on the game thread. This means that a complex generation system can be implemented without having a major impact on performance.
I then proceeded with the TBN calculations for the procedural mesh. I decided to render the global height data to a texture which is displayed on the UI with live updates. I added particle-based and grid-based methods for erosion running on the global height data (also asynchronously). When the calculations are done, the sections are updated individually similar to how they were generated.
Finally, I designed a landscape material using Megascans assets that is also fully procedural: grass-rock layer masks are calculated using slope angles (using normals), while beach-grass masks are extracted from the height data (Z vertex position in world space).
For foliage generation, I used Unreal’s build-in foliage system using foliage types and foliage spawners with some tweaks in the C++ layer. Similar to the material, it also uses height data and normals for placement. The main advantage of this system is that it has been optimized to handle millions of mesh instances, as well as to support asynchronous generation.
I also added an (optional) island modifier using a square gradient that’s generated using the Chebyshev distance from the center point with use parameters adjustable by the user. I added water using Unreal’s water system for a more realistic look.
A randomize and seed options are exposed to the UI so that the seeds for the noise generator can be easily changed.
Erosion
I implemented both grid-based and particle-based hydraulic erosion, with particle-based producing slightly better results. Below is a comparison between parameters and the resulting terrain.
Iterations: 16384
Water: 10
Evaporation: 1
Acceleration: 0.6
Sediment: 10
Iterations: 65536
Water: 10
Evaporation: 0.5
Acceleration: 0.5
Sediment: 10
Iterations: 65536
Water: 20
Evaporation: 0.5
Acceleration: 0.9
Sediment: 15
Future updates
While the current system stores and uses global height data, this could be eliminated in the favor of only having to use local height data for each sections (in other words, make the height data in one section not depend on the data in neighboring sections): The physically based hydraulic erosion could be substituted for more noise functions that fake the look of erosion, or split into separate regions called watersheds using a voronoi mask. Additionally, the global height visualization texture could be removed or confined to a set region of sections specified by the user.
If the need for global height information is eliminated, this system could be easily expanded to support infinite terrain generation.
A biome system could be added by generating a separate noise-based mask. Biomes could each have unique features, not just in the height information, but also in the materials used and foliage or structures generated.