
Creating Dynamic Water with Vertex Shaders in Unity
July 1, 2026
Layered Sound Design in Unity (Complete Guide)
July 1, 2026Creating realistic ocean waves is a challenge in many games. Simple sine waves can animate water, but they often look too uniform and artificial.
A more advanced technique used in many modern games is Gerstner Waves. This mathematical model simulates how real ocean waves move and allows water surfaces to look far more natural.
What Are Gerstner Waves?
Gerstner Waves are a mathematical wave model commonly used in real-time graphics to simulate ocean surfaces.
Unlike simple sine waves that only move vertices vertically, Gerstner waves displace vertices in three dimensions.
- Vertical movement (height)
- Horizontal movement along the wave direction
- Natural curved wave shapes
This creates much more realistic ocean motion.
Basic Gerstner Wave Formula
The simplified formula for Gerstner wave displacement is:
x = x + (direction.x * amplitude * cos(k * position + time))
y = amplitude * sin(k * position + time)
z = z + (direction.y * amplitude * cos(k * position + time))
Where:
- Amplitude controls wave height
- Direction controls the direction of the wave
- k controls wavelength
- Time animates the wave
Gerstner Wave Shader Example
Below is a simple vertex shader implementation for Gerstner waves.
Shader "Custom/GerstnerOcean" { Properties { _Amplitude ("Wave Height", Float) = 0.5 _Frequency ("Wave Frequency", Float) = 1.0 _Speed ("Wave Speed", Float) = 1.0 _Direction ("Wave Direction", Vector) = (1,0,1,0) _Color ("Water Color", Color) = (0,0.5,1,1) } SubShader { Tags { "RenderType"="Opaque" } Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag float _Amplitude; float _Frequency; float _Speed; float4 _Direction; float4 _Color; struct appdata { float4 vertex : POSITION; }; struct v2f { float4 vertex : SV_POSITION; }; v2f vert(appdata v) { v2f o; float2 dir = normalize(_Direction.xz); float wave = dot(v.vertex.xz, dir) * _Frequency + _Time.y * _Speed; float height = sin(wave) * _Amplitude; float offset = cos(wave) * _Amplitude; v.vertex.x += dir.x * offset; v.vertex.z += dir.y * offset; v.vertex.y += height; o.vertex = UnityObjectToClipPos(v.vertex); return o; } fixed4 frag(v2f i) : SV_Target { return _Color; } ENDCG } } }
Combining Multiple Gerstner Waves
Real oceans contain many overlapping waves. To simulate this, developers usually combine several Gerstner waves with different parameters.
Example concept:
wave1 = Gerstner(direction1, amplitude1)
wave2 = Gerstner(direction2, amplitude2)
wave3 = Gerstner(direction3, amplitude3)
finalHeight = wave1 + wave2 + wave3
Using multiple waves creates more chaotic and natural ocean motion.
Tips for Realistic Ocean Water
- Use multiple Gerstner waves with different directions
- Add foam near steep wave peaks
- Combine with normal maps for small surface ripples
- Use reflection probes for realistic lighting
- Add shoreline foam near land
Performance Considerations
Because Gerstner waves run inside the vertex shader, they execute on the GPU. This allows thousands of vertices to animate with minimal performance impact.
However, very dense meshes may still affect rendering performance, so balance mesh resolution with visual quality.
Conclusion
Gerstner Waves are one of the most widely used techniques for creating realistic ocean water in real-time graphics. By combining multiple waves and running the calculations on the GPU, developers can achieve convincing ocean surfaces with excellent performance.
This technique is commonly used in open-world games, sailing games, and large ocean environments.










