-
Notifications
You must be signed in to change notification settings - Fork 2
Water Rendering
SparkEngine provides a water rendering system with Gerstner wave simulation for realistic ocean and lake surfaces. Water planes are tessellated grids whose vertices are displaced each frame by a sum of Gerstner wave components, with CPU-side height queries for gameplay interactions like buoyancy and splash effects.
Source: SparkEngine/Source/Graphics/WaterRenderer.h
Namespace: Spark::Graphics
Tests: Tests/TestWaterRenderer.cpp (6 test cases)
- Overview
- Water Settings
- Gerstner Waves
- Water Plane Management
- Height Queries
- GPU Buffers
- API Reference
- Usage Example
- Integration
- See Also
The water renderer creates tessellated grid meshes for each water body and animates them using Gerstner wave summation. Gerstner waves produce the characteristic peaked crests and flat troughs of real ocean waves, unlike simple sine waves.
┌──────────────────────────────────────────────────────────┐
│ WaterRenderer │
│ │
│ WaterSettings ──► GenerateDefaultWaves() │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────┐ │
│ │ Gerstner Wave Components (N waves) │ │
│ │ direction, amplitude, wavelength, │ │
│ │ speed, steepness │ │
│ └──────────────┬──────────────────────────┘ │
│ │ │
│ ▼ │
│ Update(dt) ──► ComputeGerstnerDisplacement(x, z, t) │
│ ComputeGerstnerNormal(x, z, t) │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────┐ │
│ │ WaterPlane (tessellated grid) │ │
│ │ vertices[] updated each frame │ │
│ │ basePositions[] for undisplaced grid │ │
│ └─────────────────────────────────────────┘ │
│ │
│ GetWaterHeight(x, z) ──► CPU height query for gameplay │
└──────────────────────────────────────────────────────────┘
struct WaterSettings
{
XMFLOAT3 waterColor = {0.0f, 0.3f, 0.5f};
float opacity = 0.8f;
float waveAmplitude = 0.3f;
float waveFrequency = 1.0f;
float waveSpeed = 1.0f;
int waveCount = 4;
float fresnelPower = 5.0f;
float reflectionStrength = 0.5f;
float refractionStrength = 0.3f;
float specularPower = 64.0f;
float rippleSpeed = 1.0f;
bool enabled = true;
};| Setting | Default | Description |
|---|---|---|
waterColor |
(0, 0.3, 0.5) | Base surface color (linear RGB) |
opacity |
0.8 | 0 = fully transparent, 1 = fully opaque |
waveAmplitude |
0.3 | Global amplitude scale for all waves |
waveFrequency |
1.0 | Global frequency multiplier |
waveSpeed |
1.0 | Global speed multiplier |
waveCount |
4 | Number of Gerstner wave components |
fresnelPower |
5.0 | Fresnel reflection exponent |
reflectionStrength |
0.5 | Planar reflection blend factor |
refractionStrength |
0.3 | Refraction distortion strength |
specularPower |
64.0 | Specular highlight sharpness |
rippleSpeed |
1.0 | Detail ripple animation speed |
Each wave component is an independent travelling wave:
struct GerstnerWave
{
XMFLOAT2 direction = {1.0f, 0.0f}; // Travel direction (XZ)
float amplitude = 0.2f; // Vertical displacement
float wavelength = 10.0f; // Crest-to-crest distance
float speed = 1.0f; // Phase velocity
float steepness = 0.5f; // 0 = sine, 1 = sharp peak
};The Gerstner wave equation displaces vertices both vertically and horizontally, creating the distinctive peaked-crest shape:
x' = x + Σ (Q_i * A_i * D_i.x * cos(w_i * dot(D_i, [x,z]) + φ_i * t))
z' = z + Σ (Q_i * A_i * D_i.y * cos(w_i * dot(D_i, [x,z]) + φ_i * t))
y' = Σ (A_i * sin(w_i * dot(D_i, [x,z]) + φ_i * t))
Where Q is steepness, A is amplitude, D is direction, w = 2π/wavelength, and φ = speed * w.
Default waves are auto-generated from WaterSettings with varied directions and wavelengths.
Water bodies are represented as individual planes, each with its own tessellated grid:
auto& water = WaterRenderer::GetInstance();
water.Initialize();
// Add a 100x100 meter lake centered at (0, 0, 0)
uint32_t lakeId = water.AddWaterPlane({0.0f, 0.0f, 0.0f}, {100.0f, 100.0f});
// Add a 50x50 river section
uint32_t riverId = water.AddWaterPlane({200.0f, -1.0f, 0.0f}, {50.0f, 50.0f});
// Remove a water plane
water.RemoveWaterPlane(riverId);Each plane generates a grid with gridResolution vertices per side (default 32), for 32x32 = 1024 vertices per plane.
Query the animated water surface height at any world position for gameplay logic:
float height = water.GetWaterHeight(playerX, playerZ);
// Buoyancy check
if (playerY < height)
{
ApplyBuoyancyForce(playerY - height);
}Returns 0 if no water plane covers the queried position.
Access the full vertex data for custom rendering:
std::span<const WaterVertex> verts = water.GetMeshVertices(lakeId);
for (const auto& v : verts)
{
// v.position, v.normal, v.texCoord
}The renderer can create GPU vertex and index buffers for hardware rendering:
water.CreateGPUBuffers(rhiDevice);
// After Update() each frame:
water.UpdateGPUBuffers(rhiDevice);
uint32_t vertCount = water.GetGPUVertexCount();
uint32_t idxCount = water.GetGPUIndexCount();| Method | Description |
|---|---|
GetInstance() |
Singleton access |
Initialize() |
Create renderer, generate default waves |
Shutdown() |
Release all planes and resources |
SetSettings(settings) |
Update water configuration |
GetSettings() |
Read current settings |
AddWaterPlane(center, size) |
Add a water body, returns ID |
RemoveWaterPlane(id) |
Remove a water body |
Update(deltaTime) |
Advance simulation, displace vertices |
GetWaterHeight(worldX, worldZ) |
CPU height query |
GetMeshVertices(id) |
Access animated vertex data |
GetWaterPlaneCount() |
Number of active water planes |
CreateGPUBuffers(device) |
Create GPU vertex/index buffers |
UpdateGPUBuffers(device) |
Upload displaced vertices to GPU |
using namespace Spark::Graphics;
auto& water = WaterRenderer::GetInstance();
water.Initialize();
WaterSettings settings;
settings.waveAmplitude = 0.5f;
settings.waveCount = 6;
settings.waveSpeed = 0.8f;
settings.waterColor = {0.02f, 0.15f, 0.3f};
water.SetSettings(settings);
uint32_t oceanId = water.AddWaterPlane({0.0f, 0.0f, 0.0f}, {500.0f, 500.0f});
// In render loop:
water.Update(deltaTime);
// Gameplay: check if boat is floating
float waterY = water.GetWaterHeight(boat.x, boat.z);
boat.y = waterY + boat.draft;-
Physics: Use
GetWaterHeight()for buoyancy forces in Physics simulations - Audio: Trigger splash sounds when objects enter water. See Audio
- Particle System: Spawn spray particles at wave crests
- Sky Atmosphere: Water color is affected by sky reflection. See Rendering and Graphics
- Rendering and Graphics — Overall rendering pipeline
- Physics — Buoyancy and fluid interaction
- Terrain and Procedural Generation — Shoreline and riverbed terrain
- Day Night Cycle and Weather — Weather affects wave intensity
Published from bf219a7bdb9b. Edit the canonical source in wiki/.
- Documentation
- Docs route
- Wiki index
- Guides
- Tutorials
- Samples
- Examples
- API Reference
- API route
- Reference
- Build Guide
- Dependencies
- FAQ
- Changelog
- Roadmap
- Contributing
- Code of Conduct
- Home
- FAQ
- Getting Started
- Quick-Start Tutorial
- Making Your First Game
- Making Your First Multiplayer Game
- Artist Workflow Guide
- Editor Walkthrough
- Migration Guide
- How SparkEngine Works
- Architecture Overview
- Engine Architecture Flowchart
- Creating a Game Module
- Game Modules (catalog)
- Entity Component System
- Rendering and Graphics
- Physics
- Cloth Simulation
- Audio
- Input System
- Camera System
- Scripting with AngelScript
- Visual Scripting
- AI and Navigation
- Animation
- 2D Systems
- Networking
- Dedicated Server
- Multiplayer Quick Start
- Area Server Architecture
- Scene Management
- Large World Support
- Collaborative Editing
- Coroutine System
- Event System
- Event Response System
- Job System
- UI System
- UI Layout Extensions
- Localization
- Dialogue System
- Destruction System
- Replay System
- Achievement System
- Loading System
- Mod System
- Content Delivery
- Tween System
- Memory Integrity
- Gameplay Systems
- Terrain and Procedural Generation
- Save System
- Persistence System
- Day Night Cycle and Weather
- Cinematic Sequencer
- Runtime Prefabs
- SparkEditor
- Editor Tutorials
- SparkConsole
- SparkDaemon
- Shader Pipeline
- Asset Pipeline
- Asset Validation
- Asset Migration
- Game Packaging
- Online Services
- DataTable System
- Loot and Crafting System
- CSG System
- Font System
- Timer Manager
- Movie Render Pipeline
- HLOD and World Partition
- Remote Debug System
- Selection Manager
- Asset Dependency Graph
- Editor Automation
- File Watcher
- Project Templates
- System Requirements
- VR Support
- Mobile Platform
- Accessibility
- Platform Input
- Cross-Compilation: Wine Testing
- RHI Abstraction Layer
- D3D11 Backend
- D3D12 Backend
- Vulkan Backend
- OpenGL Backend
- Metal Backend
- DXR Raytracing
- Hybrid Ray Tracing
- Upscaling (DLSS/FSR)
- Render Graph
- Shader Graph
- GPU Particles
- GPU-Driven Rendering
- Volumetric Fog
- Volumetric Clouds
- Global Illumination
- Virtual Texturing
- Water Rendering
- Clustered Lighting
- Material System
- Post-Processing
- Shadow System
- Particle System
- Decal System
- Sky and Atmosphere
- Foliage System
- Mesh Shaders
- Neural Rendering
- Configuration Reference
- Performance Tips
- Benchmark Framework
- Threading Model
- Memory Safety
- Memory Management Patterns
- Build System and CMake Modules
- Profiler and Debugging
- Performance Profiling Guide
- Telemetry System
- Golden Image Testing
- Utilities
- Testing
- Codebase Statistics
- Codebase Health
- Error Handling Patterns
- Hot Reload Overview
- Troubleshooting
- Contributing
- Workflow Patterns
- Build Optimizations
- CI Reproducible Builds
- GitHub API and PR Checks
- Git Rebase Conflicts
- Clang-Format
- Code Quality Violations
- AI Bloat Pattern
- MinGW + Wine Cross-Compilation
- Live Editor Testing
- Engine & Renderer Landscape
- DuetOS Portability Catalog
- Five-Engine Analysis
- Eleven-Engine Analysis
- ThorVG / Unity Graphics Analysis
- Advanced Techniques Catalog
- Third-Party Library Evaluation
- Engine Viability Evaluation
- Engine Feature Recommendations
- Project Recommendations
- Mac Compatibility Analysis
- Codebase Observations
- Codebase Bloat Audit
- Test Suite Audit
- Documentation Coverage Audit
- ThirdParty Dependencies Audit
- Load Test Baseline
- Gameplay Systems Status
- SparkGame Module Status
- Stub and Abandoned Features
- Memory Integrity System
- Memory Safety Evaluation
- Hardware Acceleration Systems
- Jolt Physics Integration
- GPU/CPU Separation Plan
- Daemon Services Architecture
- Reflection & Polymorphism Refactoring Plan
- SparkBuild In-Tree
- Wine No-JobSystem Breakthrough
- Wine Role and Fallback Tiers