Jonatan Ebenholm
← all projects

Minecraft Terrain Engine

GPU-driven voxel terrain in C++20. Compute shaders build it, indirect draws render it.

Minecraft Terrain Engine

A high-performance, GPU-driven voxel terrain generation engine written in C++ and modern OpenGL (4.3+).

Inspired by Minecraft, this project pushes the boundaries of standard voxel rendering by offloading almost the entire generation and rendering pipeline, including 3D noise generation, surface culling, geometry assembly, and draw calls, directly to the GPU using Compute Shaders and Indirect Drawing.


Key Features

  • 100% GPU-Accelerated Pipeline: Terrain generation operates exclusively on the GPU. The CPU never touches vertex data.
  • Indirect Drawing (glDrawElementsIndirect): Drastically reduces CPU overhead by allowing the Compute Shader to populate draw commands. Thousands of chunks can be rendered with a single API call.
  • Smart VRAM Management: Uses a two-pass generation system (Atomic Counters / AppendBuffers) to tightly pack and allocate VBOs and IBOs, eliminating wasted memory padding common in voxel engines.
  • Procedural 3D Noise: Fractal Brownian Motion (FBM) with adjustable octaves, lacunarity, and persistence.
  • Spline-Based Terrain Shaping: 2D splines control terrain height and drop-offs, enabling distinct biomes and island-like structures (continentalness + erosion splines).
  • Surface Culling: Compute shaders discard completely hidden interior voxels before geometry is generated, keeping vertex counts extremely low.
  • Multi-threaded Architecture: Dedicated 60 Hz tick thread for deterministic world updates, decoupled from the render thread.

Screenshots

Mountain range transitioning into flat plains and ocean, shaped by the continentalness and erosion splines: Mountain to ocean biome transition

Cave and overhang formations cut through by 3D noise: Cave formation with water

Winding riverbed and cave openings across flat terrain, all generated procedurally: River and cave terrain

FPS counter overlay showing real-time rendering performance:


Tech Stack

ComponentLibrary / API
LanguageC++20
Graphics APIOpenGL 4.3+ (Compute Shaders, SSBOs required)
Window / InputGLFW
OpenGL LoaderGLAD
MathematicsGLM
UIDear ImGui

Project Structure

MinecraftTerrain/
├── Core/                        # GPU compute pipeline (library)
│   └── Source/
│       ├── Core.h / Core.cpp    # Compute shader orchestration
│       └── *.comp               # GLSL compute shaders
├── Program/                     # Application layer
│   ├── Include/
│   │   ├── App.h                # Main application controller
│   │   ├── Renderer/            # OpenGL context, camera, UI, shaders
│   │   ├── World/               # World state, player, physics, chunks
│   │   └── Helpers/             # Transform, InputState, hash utilities
│   └── Source/                  # Implementations + main.cpp
├── Vendor/                      # Third-party dependencies (GLFW, GLM, ImGui)
├── SpriteAtlas.png              # Block texture atlas
└── CMakeLists.txt

How It Works: The GPU Pipeline

This engine avoids the classic CPU bottleneck of voxel games by keeping all geometry data on the GPU. Each chunk follows a strict sequential compute pipeline:

Stage 1: 3DNoise.comp

Generates a raw density/block-ID field for the chunk using Simplex FBM noise. Two spline curves shape the result:

  • Continentalness spline controls the land vs. ocean distribution
  • Erosion spline controls mountain vs. flat terrain height

Outputs a 3D array of 16-bit block IDs (0 = Air, 1 = Stone).

Stage 2: SurfaceCulling.comp

Checks each voxel's 6 neighbors. Completely enclosed voxels are discarded early. Only visible surface voxels are written to an AppendBuffer (atomic scatter write). This is the primary geometry reduction step.

Stage 3: CountTriangles.comp

Reads the active surface voxel list and tallies exactly how many vertices and indices are needed for the chunk mesh. The result is read back by the CPU via an atomic counter.

Stage 4: Tight Allocation (CPU Intervention)

The CPU reads the exact vertex/index count and allocates a perfectly sized VBO and IBO. No wasted padding. This is the only CPU involvement in the geometry pipeline.

Stage 5: GeometryInit.comp

Generates the final quad geometry:

  • Emits vertices with tightly packed data: 10+10+10 bits for position, 3 bits for normal, 2 bits for UV corner, 8 bits for texture index.
  • Simultaneously writes the IndirectDrawCommand buffer using atomicAdd.

Stage 6: Render Loop

The CPU calls glDrawElementsIndirect(). The GPU reads its own previously generated draw commands and renders the terrain. No vertex data ever passes through the CPU.


Architecture Overview

┌─────────────────────────────────────────────────────┐
│  Render Thread (variable FPS)                       │
│  ┌─────────┐   ┌──────────────────┐   ┌──────────┐  │
│  │  App    │──▶│ ChunkMeshManager │──▶│ Core     │  │
│  │         │   │ (up to 5         │   │ (compute │  │
│  │         │   │  chunks/frame)   │   │ shaders) │  │
│  └────┬────┘   └──────────────────┘   └──────────┘  │
│       │                                             │
│       ▼                                             │
│  ┌─────────┐   ┌──────────────────┐                 │
│  │Renderer │──▶│  ChunkRenderer   │                 │
│  │         │   │  (indirect draw) │                 │
│  └─────────┘   └──────────────────┘                 │
└─────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────┐
│  Tick Thread (60 Hz deterministic)                  │
│  ┌─────────┐   ┌──────────────────┐                 │
│  │  World  │──▶│  Player          │ ◀── InputState  │
│  │         │──▶│  ChunkBlockMgr   │     (mutex)     │
│  └─────────┘   └──────────────────┘                 │
└─────────────────────────────────────────────────────┘

Chunk Generation Details

  • Chunk dimensions: 16 × 256 × 16 (X × Y × Z)
  • View distance: 24 chunks (circular culling, abs(x*z) > viewDistance² / 1.5f)
  • Chunks generated per frame: Max 5 (to maintain smooth FPS during loading)

Noise Parameters (tunable via ImGui)

ParameterDefault
Octaves5
Frequency0.003
Lacunarity2.0
Persistence0.5
Amplitude1.0

Memory Model

GPU resources per chunk:

BufferTypeContent
Noise mapSSBO16-bit block IDs per voxel
Active voxel listSSBOPacked 10-10-10 coordinates (AppendBuffer)
Packed vertex dataSSBO → VBO32-bit per vertex, tight allocation
Index bufferSSBO → IBO16-bit indices, tight allocation
Distance-to-air mapSSBO16-bit depth values per voxel
Density bufferSSBOFloat density field
Indirect draw commandSSBODrawElementsIndirectCommand struct

All buffers use move semantics on the CPU side (VoxelCubeMesh is move-only) to prevent accidental GPU resource duplication.


Controls

InputAction
W / A / S / DMove forward / left / back / right
SpaceMove up
CtrlMove down
ShiftSprint (5× speed: 75 m/s)
MouseLook around (0.1 sensitivity)
EToggle cursor (UI ↔ gameplay)

Getting Started

Prerequisites

  • GPU and drivers supporting OpenGL 4.3 or higher (Compute Shaders and SSBOs required)
  • C++ compiler with C++20 support (MSVC 2019+, GCC 10+, Clang 12+)
  • CMake 3.x

Building (Visual Studio)

git clone <repo-url>
cd MinecraftTerrain
mkdir build && cd build
cmake .. -G "Visual Studio 17 2022" -A x64

Open the generated .sln in Visual Studio and build the MinecraftTerrain target. The CMake configuration sets the debugger working directory to the repository root automatically so shaders and assets resolve correctly.


Roadmap / In Progress

The current Architecture_decoupling branch is refactoring class boundaries:

  • ShaderManager: Centralized shader program lifecycle (class exists, not yet implemented)
  • UIManager: ImGui panel management (class exists, not yet implemented)
  • WindowManager: GLFW window/context separation (class exists, not yet implemented)
  • Physics: Collision detection and movement constraints (class exists, not yet implemented)
  • ChunkCreator: Consolidating chunk mesh generation logic
  • Chunk Pooling: rather than allocating and destroying GPU buffers as chunks enter and leave the view distance, a fixed pool of VoxelCubeMesh objects will be pre-allocated at startup and reused. This removes per-chunk VRAM allocation overhead entirely and makes the memory footprint of the engine fully predictable.

License

MIT License

Copyright (c) 2026 Jonatan

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.