The Challenge of Realistic Water in Flight Simulation

For flight sim enthusiasts and professional training developers, water has always been one of the most difficult elements to render convincingly. A perfectly textured aircraft or detailed terrain can be instantly undermined by a flat, cartoonish ocean. In marine and coastal flight scenarios—from a helicopter approaching an oil rig to a seaplane skimming the waves—the water surface is often the most visually dominant element. Achieving photorealism requires a careful balance of rendering pipelines, shader mathematics, and artistic intuition. This guide breaks down the core techniques and workflows used by leading studios to create water that holds up under the scrutiny of both casual users and certified pilots.

Foundational Texture and Material Design

The journey to photorealistic water begins beneath the shader, with texture and material data. A single static texture map is never sufficient; modern water surfaces rely on layered, animated, and procedurally generated patterns.

Base Color and Foam Maps

The base color of water varies dramatically with depth, sediment, and lighting. Open ocean appears deep blue because water absorbs longer red wavelengths, leaving only blue light to scatter. Coastal areas with sandy bottoms shift toward turquoise, while muddy rivers appear brown. Developers must create distinct base color maps for each biome. Foam maps add white streaks around shorelines, ship wakes, and breaking waves. These are often painted as alpha masks and blended dynamically based on wave impact velocity.

Normal Maps for Micro-Surface Detail

Normal maps fake surface irregularities at the pixel level, giving water its characteristic "glitter" and rippled texture. A single static normal map looks repetitive, so modern implementations use multiple layers: a small-scale ripple map for capillary waves (driven by wind) and a larger-scale swell map for ocean swells. These are scrolled at different speeds and directions via a custom shader node setup. A valuable trick is to blend two sets of normal maps that move in opposition, creating the illusion of chaotic, non-repeating wavelets.

Procedural Wave Displacement

Beyond textures, vertex displacement is essential for large-scale wave motion. The industry standard approach uses a Gerstner wave function (often called a Cinewave or trochoidal wave). Developed to model granular particle motion in water, Gerstner waves steepen crests and flatten troughs, producing the characteristic sharp peaks of breaking waves. A typical implementation uses between four and sixteen Gerstner waves summed together, each with its own amplitude, frequency, direction, and phase. Unreal Engine, for example, supports Gerstner waves directly in its water system plugin, while custom GLSL or HLSL shaders can replicate the math for Unity or three.js.

For deeper realism, developers can compute wave steepness and trigger particle emitters when a vertex's normal exceeds a threshold—this simulates whitecaps and spindrift without needing a separate simulation.

Reflections and Refractions: The Optical Illusion of Depth

Water is a transparent medium, and a flat reflection doesn't cut it. Two separate optical effects must be handled: reflections of the sky and environment, and refractions of the underwater world (or river/lake beds).

Screen Space Reflections (SSR) vs. Planar Reflections

Screen space reflections work by ray-marching through the rendered frame to find pixels that should reflect into the water. They are cheap and adapt naturally to dynamic light and moving objects. However, SSR fails when reflecting objects that are off-screen or behind the camera. For marine flight scenarios, this is a common problem because the player often looks down at the ocean while the sky and coastline are above and behind. A reliable solution is to combine SSR with a static or dynamic cubemap that fills in the missing data. Planar reflections, which render the scene again from the water's surface plane, provide perfect reflections but at high cost. Use them sparingly—perhaps only for calm inland lakes where the mirror effect is critical.

Refraction and Subsurface Scattering

Even in shallow coastal flying, the eye expects to see distortion of underwater terrain through the water column. A refraction pass samples the background scene texture (the riverbed or seafloor) and applies a perturbation offset derived from the normal map. The magnitude of offset should increase with depth. For truly photorealistic water, subsurface scattering (SSS) is vital: light penetrates the surface, scatters within the water volume, and re-emerges. This creates the soft glow of shallow turquoise water and the deep saturated blue of the open ocean. Implement SSS as a post-process screen-space diffusion or as a custom light function in your engine of choice.

Simulating Water Dynamics at Scale

Real water never stands still. For flight simulation, the simulation must cover vast areas—an ocean that stretches to the horizon–while also handling near-field interactions like ripple rings from waves or the wake of a player’s aircraft flying at wave-top altitude.

LOD-Based Wave Systems

A single wave function cannot handle both a 10-kilometer view distance and a 10-centimeter wave detail near the player. The standard technique is a level-of-detail (LOD) approach: far from the camera, a simple sum of large Gerstner waves runs on the GPU with a coarse vertex grid. As the camera moves closer, the grid tessellation increases, and lower-amplitude, higher-frequency waves are added. CryEngine and Unreal Engine both implement this as a tiled grid of water patches that refine hierarchically. The player never notices the transition.

Particle-Based Local Effects

When a seaplane touches down or a helicopter hovers near the surface, localized water disruption is expected. This is best handled by a particle system: spray particles ejected from a collision point, foam particles that spread and fade, and ripple ring meshes that grow outward. A practical workflow uses a GPU simulation buffer that collects impact events and drives instanced sprite billboards. For river and coastal flight, also simulate current flow by applying a directional velocity to floating debris or by animating the base color map in the direction of the river.

Interaction with the Environment

Water must interact with static and dynamic objects. Beaches should show a wet/dry sand boundary that moves with the tide. Rocks and piers should generate local swirls and wakes. In many engines, this is achieved by placing invisible collision primitives that "push" the water vertices away. Alternatively, a stencil buffer approach marks areas where the water surface is disturbed and applies an additional displacement offset. For coastal flights, modeling the breaking of waves against cliffs adds immense realism: use a signed distance field of the shoreline mesh to detect shallow angle impact and activate a foam emittance zone.

Performance Optimizations Without Sacrificing Fidelity

High-fidelity water effects are known to be among the most GPU-intensive features in any real-time application. In a flight simulator, where the entire world must be rendered at high frame rates, you cannot afford to waste cycles. The following optimizations are standard practice among AAA developers.

Adaptive Tessellation and Culling

Modern GPUs support hardware tessellation, allowing you to generate triangles on the fly based on distance. Set your tessellation factor to drop from 64 at 10 meters to 1 at 1 kilometer. Combined with view-frustum culling of water tiles that are entirely behind the camera or obscured by terrain, you can keep the triangle count within budget. Additionally, use occlusion culling: if a large island is between the camera and a section of ocean, skip rendering that water tile entirely.

Simplified Physics for Distant Water

Wave simulation doesn’t need to be full Gerstner for distant patches. For tiles beyond 500 meters, switch to a simple sine-based oscillation with a directional offset. The human eye cannot perceive individual wave shapes at that distance; all it sees is a moving, reflective surface. Also consider baking the large-scale wave motion into a looping animated normal map for the far LOD level, removing vertex displacement entirely.

Instancing and Precomputation

For static bodies of water like inland lakes, precompute the wave displacement for a representative grid of points and store it in a texture. Then apply this texture as a vertex offset each frame. This is extremely fast and avoids any runtime wave calculations. For ocean environments where dynamic wave patterns are required, use compute shaders to update a displacement map on the GPU rather than on the CPU, avoiding a costly transfer from system memory.

Different engines offer different starting points. Here is a quick overview of how to achieve photorealistic water in the most common flight sim development environments.

Unreal Engine 5

UE5 includes a Water plugin with a built-in body of water actor that supports Gerstner waves, wave spectrum simulation (via FFT), and shoreline interaction. It also provides a Water Mesh component that auto-tessellates. For best results, enable Lumen for global illumination on water, as it captures the subtle color transfer from sky and terrain onto the water surface. To add foam, use the included Water material functions that blend shoreline foam based on depth. For coastal flight, the Niagara particle system can create spray and mist effects.

Unity

Unity’s High Definition Render Pipeline (HDRP) includes a Water System (as of 2022.3+) that supports both simulated and baked water. It offers configurable wave parameters, reflection probes, and refraction. By default, it uses planar reflections or screen space reflections. Unity also has the Boat Attack Water System (free on the Asset Store) that provides excellent wave interaction with objects. For custom shader work, the Amplify Shader Editor or Shader Graph can be used to implement Gerstner waves with scrolling normal maps.

Custom Engine / WebGL

For bespoke simulators or browser-based experiences (e.g., Microsoft Flight Simulator Mods or three.js), implement water as a tessellated plane with a vertex shader. A popular open-source reference is the Three.js Ocean example (available on GitHub), which implements FFT wave simulation on the GPU. Convert the FFT result into a displacement map and feed it to a vertex shader. For reflections, use a dynamic cube map rendered from the water surface or use a pre-generated skybox with parallax correction.

Case Studies: Photorealistic Water in Commercial Flight Sims

Looking at how leading products handle water can inform your own pipeline.

Microsoft Flight Simulator (Asobo Studio)

Asobo uses a combination of procedural wave functions and high-resolution aerial imagery. They sample real-world bathymetry data to determine water color and depth. Their wave system uses a spectral wave model that analyzes wind speed and direction from live weather data, then outputs a Gerstner wave sum. They also employ volumetric cloud reflections on the water surface, using ray-marched shadows—this is why the ocean in MSFS looks so realistic on overcast days. Performance is maintained by streaming water tiles in the same LOD system used for terrain.

DCS World (Eagle Dynamics)

DCS World focuses on military aircraft simulation and includes coastal and carrier operations. Their water rendering recently moved to a PBR-based shader with screen-space reflections and anisotropic highlights. They use a simplified wave simulation that reacts to wind and aircraft proximity. For carrier landings, they precompute specific wave patterns that match the deck motion, ensuring the ocean surface aligns with the ship’s pitch and roll.

Conclusion: The Pursuit of Unbroken Immersion

Photorealistic water in marine and coastal flight scenarios is not a single trick but a carefully orchestrated stack of texture, shader, simulation, and optimization techniques. The best results come from understanding the physics of light and wave motion, and faithfully reproducing them within the constraints of real-time rendering. Start with a strong normal map and Gerstner wave foundation, layer in reflections and refractions, add local particle interactions, and always keep an eye on performance budgets. By following the principles outlined here, you can create water that doesn’t break the spell—whether you’re training a pilot for a seaplane landing or simply enjoying a calm flight over a Caribbean coastline.

For further reading, consult the official documentation on Unreal Engine’s water system (Unreal Engine Water System), the GPU Gems 3 chapter on water simulation (Gerstner Waves on GPU Gems), and the Unity HDRP water sample (Unity HDRP Water System).