Procedurally Generated Water Shader in UE4
A procedural water shader based on GPU Gems
Research Paper
GPU Gems, Chapter 1. Effective Water Simulation from Physical Models, NVIDIA
Github Link
Final Result
The final result of the procedural water shader
Brief Introduction
This project is mainly trying to research and implement water shader based on GPU Gems Chapter 1 , the goal for the project is to create a believable, variant water shader that can provide dynamic visual looks, with large seamless scale water volume yet still have decent performance that can run on a modern GPU at realtime.
Materials and Material Functions Structure
Core Material
MODULE BREAKDOWN
Basic Mathematics / Algorithms / Terms
The biggest challenge is to implement the shader in Unreal, by manipulating the water surface vertex in real-time.
According to the book, the key to simulate realistic water waves is to blend multiple sine waves together, then manipulate them to give them a more natural look. (Gerstner Wave function for example)
Base Color
Firstly, base color is simply just a tint of distorted scene color (which creates a refraction effect, see “Refraction” below), then it is interpolated by a depth test, to mimic the feeling of depth.
Mathematically Generated Height: Gerstner
Since the wave function is using Gerstner: Mathematically Generated Height
This function can be translated to unreal hlsl by:
1
2
3
4
float3 p;
p = WavePos;
p = float3( p.x + CigmaX, p.y + CigmaY, CigmaZ );
return p;
Where float3 WavePos; float CigmaX; float CigmaY and float CigmaZ are all inputs from outside
Procedurally Generated Normal
The normal will be:
Which is the cross product of a vertex’s Binormal and Tangent, in Unreal hlsl:
1
return float3(-1. DDX, -1. DDY, 1.);
Here, DDX H(X,Y,T) is used to calculate the mix of all four curves:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Wavelength
// Speed
// Direction
// K
float Phi;
float Freq;
//float w;
float Px;
float3 WorldPos;
float2 Dir;
Dir = Direction;
Freq = sqrt(9.8 2 3.1415 / Wavelength);
Phi = Speed * Freq;
WorldPos = GetWorldPosition(Parameters);
float tempFunc1;
tempFunc1 = (sin((dot(Dir, WorldPos.xy)) Freq + Time Phi) + 1.) / 2.;
float tempFunc2;
tempFunc2 = cos((dot(Dir, WorldPos.xy)) * Freq + Time * Phi);
Px = K * Freq * Dir.x * Amp * pow(tempFunc1, (K - 1)) * tempFunc2;
return Px;
Distance-Based Tessellation
Refraction
Refraction is using a distort of the sample uv of background scene color buffer: