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

Hydraulic erosion between chunks #1

Closed stephen-hqxu closed 3 years ago

stephen-hqxu commented 3 years ago

Current state

Bug caused

Suggestions

Possible implementation

stephen-hqxu commented 3 years ago

Hydraulic erosion between chunks is definitely not something easy to implement. In the future we will refer it as free-slip hydraulic erosion.

Here's some dev notes.

General approach

When rendered chunks are requested to be loaded, all visible chunks are checked for completeness. If not, for each of those chunks, the program will also check for its surrounded chunks. If any of the surrounded chunk is not found, it will be inserted and computed for heightmap. Whenever it's computable, all neighbour chunks are locked such that no other threads can copy them until it's released, and make a copy of all heightmaps, sent to device.

For all chunks, the simulator will only spawn rain drop at the center chunk, while the neighbour chunks will hold rain drop that flows out of the boundary of the central chunk.

For simplicity, central chunk is contained in neighbour chunks

Global-Local index table

When we copy neighbour chunks, chunks are aligned linearly, called local index, where the index of the next free-slip chunk starts at the end-index of the previous chunk+1. When chunks are eroded, the rain drop system uses global index system, where index is continuous within a row of a free-slip range, such that the index of the first element of each row of the chunk is the index of the left-hand side element+1.

For more information please refer to the documentation in the source code.

Instead of arranging the chunk into global index system, we use some math formula to convert the index during erosion. To make things more efficient, we pre-compute a lookup table for such index.

What's going on?

Currently we can confirm that water droplet can freely slip to the neighbour chunks. There are a few bugs in the current release, and possible solution for each.

stephen-hqxu commented 3 years ago

Some more suggestions

stephen-hqxu commented 3 years ago

We introduced a new algorithm for generating an interpolation table instead of using the previous interpolation method which used a plenty of branches to check for areas required for interpolation.

Official release will go live soon, hopefully. We are still working on host side codes to ensure data coherence.

Interpolation index lookup table.

The lookup table uses thread ID as index, and lookup for the coordinate (x,z) for required for interpolation. Compared to out old brute-force search method, it not only cuts down the number thread required for such operation, but also utilises all threads.

Checklist Item Brach Checking (Old) Specialised Table (New)
Type Conditionals Mathmatical formulae
Number of thread lauched freeslip^2 * dimension^2 (dimension - 2) (2 freeslip * (freeslip - 1)) + (freeslip - 1)^2
Thread utilisation ~1% depends on freeslip and dimension, smaller utilisation when both are greater Always 100%
Data security Required even more branches to make sure threads don't cross over to other working areas Interpolation areas are predefined in the table and only simple branches are needed to distribute working threads

The old algorithm:

if (pixel_requiring_interpolation) {
   perform_interpolation();

} else {
   return;
   // Threads that are not in the interpolation pixel will be killed
}

void perform_interpolation(float* maps) {
   // A lot of braches to determine how to do interpolation...
}

How does the table works

We classify interpolation into 3 types:

Patch will not cross over to other patch nor the edge of the freeslip map so the current freeslip chunks can still connect to adjacent area that are not presented. Bits are inserted into the MSB of (x,z) coordinate of the interpolation pixel, and we use combination of both bits to determine what type of interpolation it's, and three simple branches are used for each type.

For more information please refer to the documentation within source code