stephen-hqxu / superterrainplus

SuperTerrain+: A real-time procedural 3D infinite terrain engine with geographical features and photorealistic rendering.
MIT License
12 stars 1 forks source link

Normal map border seam #17

Closed stephen-hqxu closed 3 years ago

stephen-hqxu commented 3 years ago

Bug description

Around the edge of the terrain normal map, some distinct and strange color band can be observed. Although the terrain mesh itself is connected, normalmap is not, sharp line can be identified at the border of two chunks.

Cause

Rendering textures are stored in a texture array, each layer of texture is independent of each other. OpenGL has no idea how to do linear filtering for texture coming from two different layer, hence it's not continuous. Effect goes away when filtering uses nearest settings.

Suggestion

It's a bad idea to use texture array if each texture is related to each other. Consider dumping all texture into a single rendering buffer. There is another benefit of doing this which is no need to do post-erosion interpolation, because chunkID logic in glsl can be discarded as well, and all vertices share the same texture, so they are all connected.

stephen-hqxu commented 3 years ago

Normal map seam is now resolved completely after the recent release.

A brief comparison

Here's a brief comparison among the original erosion, free-slip erosion introduced in #15, intermediate result after merging the rendering buffer into a single texture and the final selective edge copy algorithm.

As shown a bumpy border can be seen in the original free-slip erosion.

After merging all texture, heightmap is interpolated automatically, yet there are still some strange normalmap coloration due to incosistent neighbour. For example, chunk a was the centre of a set of neighbour chunks S, but later chunk a becomes the neighbour chunks S' of another centre chunk b, so the border of a is no longer seamless with its original neighbour because neighbour has changed and recomputed.

To mitigate this, we simply don't recompute the border of the edge of free-slip range, so the border of the edge chunks are still consistent with their original neighbours. Since rendering buffers stored on host side are recomputed, we need to copy rendering buffer.

There are 3 approaches to copy host rendering buffer:

Algorithm Method No. Copy API Called Size of Data Copied
Copy the Texture Simply copy everything, include all central pixels that will be overwritten. 1 mapSize.x .z freeslipChunk.x .z sizeof(uint16)
Copy the Border Copy the border of each individual chunk. freeslipChunk.x .z 4 (mapSize.x + (.z - 2)) 2 freeslipChunk.x .z sizeof(uint16)
Copy the Edge Copy the edge of the entire free-slip neighbour. (freeslipChunk.x + .z ) * 2 (mapSize.x freeslipChunk.x + (mapSize.z freeslipChunk.z - 4)) * sizeof(uint16)

If overlapping pixel exists, we always use row copy to cover that due to concern about cache.

Figure 1: erosion4 0

Figure 2: erosion3 0

We finally decided to use Copy the Edge algorithm, and implemented a lookup table to determine what copy mode to use at the current chunk.