When generating large or infinite terrain, it is impractical to render it all using one giant mesh. Most terrain systems use what’s called “chunking”: splitting the terrain up into chunks to split the generation over multiple threads or frames. If the player moves in a far enough in a certain direction, new terrain chunks will be spawned in front of the player and the furthest chunks will be deleted.
While writing my infinite terrain system, I ran into an annoying issue. I had everything else working as I wanted: the height of the mesh was being generated for each chunk, the chunks would move in front of the player and recalculate as the player moved. However, on the edge of each chunk, I noticed seams. I went back and confirmed that the height of the mesh was correct, so it had to be something else. It turned out to be the normals at the edges were being calculated differently per mesh!
This is what the seams look like:
In my procedural terrain implementation, the normals for each mesh vertex are calculated using the surrounding vertices. Here’s a snippet of the code:
A consequence of this method is that normals are limited to the vertices of the containing mesh. When two meshes are placed next to each other, the heights of the vertices may be the same, but the normals come from different sources. Thus, when two edges are placed side by side, the only way to get seamless results is for them to share normals.
The objective has been set: implement a method that allows terrain chunks to share vertices. Now, on to the implementation.
The implementation I chose is a simple one. The chunks in my terrain are square grids. Since the issue occurs on the edge of the grid, my solution was to create a grid that extended one row and column in each direction. This creates an overlap since the extended grids are larger than the terrain chunks, and thus the chunks effectively “share” vertices. The actual mesh would use a subset of this grid to generate the terrain.
Here is an illustration of what I’m talking about:
And here is some relevant code:
This is the exact same code for generating a normal grid, with one change: I add 2 to the
gridSize because each axis is extended by one row or column on each side, and there are two sides per axis (left/right for x axis, top/bottom for y axis).
Now that the mesh chunks have overlapping grids, they share normals and the problem is resolved.
This is what the seamless terrain looks like:
Overall, I was happy that I found a solution to my problem, but I know that it can be improved greatly. Due to time constraints and more pressing issues, I moved on after my initial solution.
The biggest red flag is that some of the mesh data is duplicated across multiple chunks. This could be solved by caching the data in arrays that are generated when new chunks are needed. I could also look for a different method for calculating the normals that did not rely on the mesh data.
Thanks for reading!