Real-time ocean surface simulation using the Fast Fourier Transform, built with WebGPU (C++). Developed as a bachelor's thesis project.
This project implements a physically-based, real-time ocean surface simulation running entirely on the GPU via compute and render shaders written in WGSL. It is built with C++23 and the WebGPU graphics API, targeting both native desktop (wgpu-native / Dawn backends) and the web via Emscripten. The ocean model is based on the statistical JONSWAP wave spectrum evolved over time using a GPU-accelerated 2D Inverse Fast Fourier Transform.
At startup the CPU generates a statistical wave spectrum h0(k) using the JONSWAP directional model. Each frequency component is seeded with a Gaussian random amplitude scaled by the spectral energy density, which depends on wind speed, fetch length, and the peak enhancement factor γ. The Hermitian symmetry condition h0(−k) = h0*(k) is enforced so the IFFT output remains real-valued.
Each frame a compute shader evolves the spectrum:
where ω = √(gk) is the deep-water dispersion relation. The same pass simultaneously computes the slope spectra (∂h/∂x, ∂h/∂y) and choppy displacement spectra (Dx, Dγ) in the frequency domain by multiplying by ik.
The six frequency-domain textures are transformed to the spatial domain by a two-pass 2D IFFT: horizontal butterfly passes followed by vertical butterfly passes. The Cooley-Tukey DIT algorithm is used with a precomputed twiddle-factor lookup table stored in a texture. Results are written into ping-pong RGBA32Float textures each frame.
A separate compute pass computes the full ×ばつ2 Jacobian determinant of the displacement field via central finite differences:
Where J < threshold, wave crests are breaking and foam accumulates proportionally. A configurable erosion factor decays the foam field each frame, producing a natural fade-out between breaking events.
The water surface is rendered as a ×ばつ256 mesh tiled in a ×ばつ3 grid (9 GPU instances) for a seamless infinite-ocean appearance. Each frame:
- The vertex shader samples height, Dx, and Dγ textures to displace vertices in all three axes
- The fragment shader computes surface normals from slope textures, then evaluates:
- Blinn-Phong diffuse + specular (directional sun)
- Schlick Fresnel for view-dependent reflectivity
- Cubemap environment sampling along the reflected view vector
- Foam blending using the Jacobian-based foam mask and a tiling detail texture
The skybox is rendered in a single fullscreen triangle with depth LessEqual and no depth writes, filling the background after the water geometry.
Runtime parameters are exposed through ImGui panels:
| Panel | Parameters |
|---|---|
| Ocean | Choppiness (λ), patch size, wave amplitude, wind speed X/Y, fetch — plus a Rebuild spectrum button to regenerate h0(k) after JONSWAP changes |
| Foam | Jacobian threshold, erosion rate, accumulation scale |
- 🌊 JONSWAP directional wave spectrum with configurable wind, fetch, and peak enhancement γ
- ⚡ GPU-accelerated 2D IFFT via Cooley-Tukey butterfly algorithm (×ばつ16 workgroups)
- 🫧 Jacobian-determinant foam with proportional accumulation and exponential erosion
- 🌅 Cubemap skybox with Fresnel-based environment reflections
- 🧩 ×ばつ3 seamless tile instancing for an infinite-ocean appearance
- 🎛️ Real-time ImGui parameter panels with live feedback
- 🌐 Dual target: native desktop (wgpu-native / Dawn) and web (Emscripten / WebGPU)
cmake -B build-wgpu -DWEBGPU_BACKEND=WGPU cmake --build build-wgpu
cmake -B build-dawn -DWEBGPU_BACKEND=DAWN cmake --build build-dawn
emcmake cmake -B build-emscripten cmake --build build-emscripten
| Resource | Used for |
|---|---|
| LearnWebGPU — Elie Michel | WebGPU C++ setup, webgpu-utils.h, general API patterns |
| WebGPU Fundamentals | WebGPU concepts and reference |
| WebGPU Samples | Shader and pipeline examples |
| Emscripten | Web / WASM build toolchain |
| Jerry Tessendorf — Simulating Ocean Water (2001) | JONSWAP spectrum, FFT ocean model, Jacobian foam derivation |