...and the equivalent C#.
Vector3 GerstnerDisplacement(Vector3 pos, Vector3 direction, float amplitude, float phase, float timescale, float offset, float gravity, float depth)
{
float period = offset * timescale;
float theta = Theta(pos, direction, amplitude, phase, period, gravity, depth);
// X
float a = direction.x / direction.magnitude;
float b = amplitude / HyperbolicTangent(direction.magnitude * depth);
float x = -1f * (Mathf.Sin(theta) * a * b);
// Y
float y = Mathf.Cos(theta) * amplitude;
// Z
float j = direction.z / direction.magnitude;
float z = -1f * (Mathf.Sin(theta) * j * b);
return new Vector3(x, y, z);
}
float Theta(Vector3 pos, Vector3 direction, float amplitude, float phase, float offset, float gravity, float depth)
{
float frequency = Frequency(direction.x, gravity, depth);
float a = (direction.x * pos.x) + (direction.z * pos.z);
float b = frequency * offset;
return (a - b) - phase;
}
float Frequency(float length, float gravity, float depth)
{
float a = gravity * length;
float b = HyperbolicTangent(length * depth);
return Mathf.Sqrt(a * b);
}
float HyperbolicTangent(double a)
{
return (float)System.Math.Tanh(a);
}
For each buoyant object, I compute the distance under the wave height every phyisics tick and then apply an upwards force proportional to that distance.
Note that because Gerster waves displace horizontally as well as vertically, the y component of the displacement vector is not perfectly accurate to the provided x and y position.
In my case this was not noticeable in the final product, however you can attain a more accurate result by subtracting the x and z offsets from the original position and then doing the Gerstner computation again.
Somewhere around four itteration of this gives a very accurate aproximation.