Terrain chunks + LOD rings — from framerate disaster to playable
The terrain system generated beautiful chunks. It also dropped the framerate to 12fps when more than three chunks were visible. The problem was obvious: every chunk was rendered at full resolution regardless of distance.
The LOD approach
Each terrain chunk now exists in three detail levels:
- LOD 0 (full): 64x64 grid, ~8192 triangles. Used when the chunk is within 2 chunks of the player.
- LOD 1 (half): 32x32 grid, ~2048 triangles. Chunks 2-5 away.
- LOD 2 (quarter): 16x16 grid, ~512 triangles. Everything beyond 5 chunks.
In Godot 4, I implemented this with a single MeshInstance3D per chunk and a LOD manager that updates mesh references when the player crosses chunk boundaries.
func _update_lod(chunk: TerrainChunk, player_pos: Vector3) -> void:
var dist = chunk.position.distance_to(player_pos)
var lod = 0 if dist < LOD1_DIST else (1 if dist < LOD2_DIST else 2)
if chunk.current_lod != lod:
chunk.set_mesh(lod_meshes[chunk.id][lod])
chunk.current_lod = lod
The seam problem
LOD transitions create visible seams — the high-detail chunk has vertices that don’t align with the low-detail chunk’s edge. Standard solution: skirts. Each chunk gets extra triangles along its edges that extend downward, hiding the gaps between mismatched LOD levels.
The skirts are generated at chunk creation and don’t change with LOD — they always match the lowest-detail neighbour.
Result
Framerate went from 12fps (8 visible chunks) to 60fps (28 visible chunks). The LOD transitions are visible if you look for them, but acceptable in gameplay. The skirts work — no visible gaps at normal camera heights.
Next: I want to try a different approach for LOD transitions — blending between LOD levels over a few frames rather than snapping. That will require a shader.