I have been experimenting with terrain displacement mapping, basically taking a rock texture and using the bump channel to displace the terrain. I had problems recreating the vertex shader in gdscript so I made a compute shader over the last 2 weeks (slow progress since I have been busy with Real Life issues).
So basically I am at the stage where I am mostly satisfied with the result, the collision mesh is lining up with the visual terrain … mostly … there may still be floating point precision issues but its lining up mostly … and generating in chunks surrounding the player. I am just having trouble using the Physics Server and am not sure how to use threads with the compute shader. I would ideally like to do all the triangle processing in a thread with the compute shader in the main thread, or, I could do all the processing in a thread.
and it hopefully isn’t completely unreadable … the essence of the project is that grid meshes of the same scale and different LOD are instanced in a grid. The visibility ranges were set manually and can easily be tweaked.
The meshes all share the same material - its just a shader that displaces the vertices to match a heightmap texture. I then do the displacement mapping trick with the bump textures of the rocks and grass.
Because your terrain is generated from a heightmap, I would avoid creating a full triangle collision mesh.
Use a lower-resolution HeightMapShape3D for each nearby chunk instead. Generate only the height values, then assign them to map_data. It is faster than a ConcavePolygonShape3D and should match this type of terrain well.
Generate the height data with WorkerThreadPool, but apply the finished shape to the CollisionShape3D on the main thread using call_deferred(), since the active scene tree is not thread-safe.
If you keep the compute shader, use buffer_get_data_async() rather than sync() or buffer_get_data(), then create the collision when the callback returns.
I would also keep collision LOD separate from visual LOD, reuse existing chunks, and only update them when the player enters a new chunk rather than every frame.
Hi, yeah I agree the Heightmap shape works better than a triangle mesh but the displacement mapping is moving the vertices off the grid so they can even overhang - they are no longer a heightmap shape.
I can always regress the project to use heightmap shapes in chunks yes … and that does work well.
You are right: once the vertices can fold over the XZ grid and create overhangs, a HeightMapShape3D can no longer represent the terrain correctly.
In that case, I would use a lower-resolution ConcavePolygonShape3D for each nearby chunk. Generate the PackedVector3Array of triangles in a background thread, then create and assign the collision shape on the main thread.
Only rebuild collision when the player enters a new chunk or when the terrain changes, and reuse the existing chunk nodes instead of recreating everything.
If you keep the compute shader, avoid reading its result back immediately, since GPU synchronization may cause a larger stall than generating the collision on the CPU. Another option is a hybrid setup: heightmap collision for normal ground and simpler convex colliders for overhangs.
I was aware of this, however the problem is architectural, its a complicated system however not difficult to understand on the function level …
The _process(delta) function checks the player/camera position every frame and if the grid cell has changed (they are 128 x 128) then the collision generator is called.
The function loops over the 9 nearby grid cells and checks whether they already have collision models, if they do not then it calls a function that generates the mesh. This function either uses threads (CPU) or compute (GPU). That is all fine and working properly.
The compute function looks like
compute.execute(...)
compute.sync()
var collision_verts = compute.read_buffer(...)
## then form the triangle list from the vertices and indices
so the return value is from compute.read_buffer(…) but that must happen after sync() so how do I call sync() and retrieve the returned values from a thread ?