The second version of a C++ voxel engine, focused on making a larger procedurally generated world run smoothly in real time.
Version 2 replaces the original single-noise terrain generator with layered terrain and climate-driven biomes, while rebuilding chunk streaming, rendering, and memory management around measured performance.
- 3.9x
- Faster chunk loading
- 16x
- Less memory per chunk
- 79%
- Fewer draw calls
Performance

A custom F3 performance overlay tracks frame time, chunk generation and meshing time, and the number of pending, loaded, visible, and rendered chunks.
The measurements drove the main optimization work instead of relying on visual or subjective performance improvements.
Chunk Streaming
Parallel Generation
Terrain generation and mesh construction were moved off the OpenGL thread and distributed
across a persistent std::thread worker pool.
Because terrain is deterministic from world position and seed, chunks can be generated independently without shared world state.
147 ms → 37 ms per chunk batch
A separate 3 ms per-frame build budget prevents large streaming batches from causing frame-time spikes when crossing chunk boundaries.
Chunk Unloading
Chunks outside a configurable keep distance are unloaded and their GPU buffers released, preventing memory usage from growing indefinitely as the player explores.
Edited blocks are replayed when a chunk is regenerated, preserving player changes across streaming.
Rendering
Frustum Culling
Chunks outside the camera's view frustum are rejected using bounding-box tests against the six planes of the view-projection matrix.
At a render distance of 9:
361 chunks → 76 chunks drawn
This reduced draw calls by 79% while preserving the visible scene. The same frustum system is also used for shadow rendering with the light's view-projection matrix.
Greedy Meshing
Visible block faces are merged into larger quads using a 2D greedy-meshing pass.
Across 60 generated chunks:
| Before | After | |
|---|---|---|
| Quads | 33,168 | 14,088 |
| Vertices | 132,672 | 56,352 |
| Indices | 199,008 | 84,528 |
The result is 2.35x fewer quads and 57% fewer vertices and indices.

Memory
Each voxel originally stored its own world position alongside its block type.
Since a block's position can be derived from its index within a chunk, the redundant position data was removed and block types were reduced to a byte-sized representation.
405 KB → 25 KB per chunk
That is a 16x reduction in chunk memory.
Combined with chunk unloading, memory usage now remains bounded during long-distance exploration.
Procedural World Generation
Layered Terrain
The original single Perlin height map was replaced with layered noise fields for:
- Continents
- Mountain ranges
- Hills
- Peaks
- Rivers and lakes
- Fine terrain detail
- Temperature and moisture
Domain warping adds larger-scale variation while keeping generation deterministic from
(x, z) and the world seed.

Climate-Driven Biomes
Biome selection is based on temperature, moisture, elevation, and a variation field.
12 biomes are defined through data rather than hard-coded generation logic, controlling surface blocks, vegetation density, and terrain tint.

Result
Version 2 turned the engine from a functional voxel renderer into a more scalable real-time system.
3.9x faster chunk loading · 16x lower chunk memory · 79% fewer draw calls · 2.35x fewer mesh quads
The main theme throughout the rebuild was profiling-driven optimization: identify the expensive stage, change the system structure, and measure the result.
Stack
- C++
- OpenGL
- GLM
- FastNoiseLite
- std::thread
- CMake