Terrain Generation

Project is WIP


This write-up showcases an in depth exploration of procedural terrain generation that I completed as an independent study during the Spring 2024 semester. I chose to develop in C++ using the WebGPU graphics API.

Learning Outcomes

What is WebGPU?

WebGPU is the modern successor to WebGL, providing far better compatibility with modern GPUs, support for GP GPU computations, and access for more advanced GPU features on the web. C++ implementations of the API, such as Google’s Dawn or WGPU-native can be used to write cross-platform desktop applications and still build to the web using Emscripten. WebGPU has several layers of abstraction, but most notably it adapts to the device’s native GPU API including DirectX, Vulkan, OpenGL, and Metal. Overall, WebGPU has a similar learning curve, more modern GPU features, and greater device compatibility compared to OpenGL/WebGL.


  1. Camera Controller (zoom, click to drag, etc.)
  2. Dev GUI
  3. Generate plane meshes of configurable sizes
  4. Adjust mesh using noise as a heightmap
    • Explore Value, Perlin, Simplex, and Cubic noise varieties.
    • Sample multiple noises at once (Fractional Brownian Motion)
    • Adjust noises individually including scale, amplitude, etc.
  5. Color map the terrain based on height, steepness, etc.
  6. Dynamically load and unload chunks of terrain
  7. Dynamically adjust level of detail a quadtree, clip maps, etc.
  8. Cull the terrain using frustum and occlusion
  9. Atmospheric effects
    • Lighting
    • Skybox
  10. Vegetation
  11. Water
  12. Post Processing Effects

Week 0: Learning WebGPU

During my first week, I spent most of my time learning the API. I first created a color picker with an accurate color wheel to select from. I then created a 3d rotating cube to exercise the use of uniforms and projection.

I learned several things during this part, including how to debug buffer data, meshes, and textures on the GPU using RenderDoc.

At this point a camera controller can now be built.

Week 1: Camera Controller

There are many different kinds of cameras that can be implemented:

  • First person
  • Turntable
  • Trackball

For the purposes of this project, a turntable will be used. A turntable camera orbits around a focus point that it remains centered on. Unlike a trackball camera that orbits without any notion of “up”, a turntable camera has a fixed up vector.

A Window class is used to manage GLFW’s input callbacks. Upon execution, it will call the application’s corresponding input method allowing for rotation and zoom using the mouse.

For example:

void Window::glfwScrollCallback(GLFWwindow *window, double xOffset, double yOffset) {
  auto* app = reinterpret_cast<Application*>(glfwGetWindowUserPointer(window));
  if (app) {
    app->onScroll( {xOffset, yOffset} );
void Application::onScroll(vec2 scrollOffset) {
  camera.zoom += camera.scrollSensitivity * scrollOffset.y);
  camera.zoom = clamp(camera.zoom, -2.0f, 2.0f);

The camera is defined using spherical coordinates (r, theta, phi), where r is the distance to the center, theta is the rotation about the vertical y-axis, and phi is the rotation about the horizontal plane.

These values are then converted to cartesian coordinates (x, y, z), corresponding to the camera’s position. The GLM function lookAt is then used to rotate the camera towards a fixed point (in this case the world origin).

Week 2: The Plane

To generate a 2D plane, the vertices are written left to right, bottom to top. An index buffer is then used to look up the vertices for any given triangle. This removes the need to repeat floating point vertex values when they are shared among multiple triangles.

The mesh must be regenerated when the size or wireframe mode is changed. Since WebGPU does not have a native wireframe toggle, the index data must be recalculated using line segments instead of triangles.

Demo of camera system and 2D mesh creation.


The following references were used in the development in this project:



Fine Detail

Level of Detail

Terrain culling: Frustum, occlusion


This post is licensed under CC BY 4.0 by the author.