This is the code I have been using...
void GerstnerWaveTessendorf(float aTime, float waveLength, float speed, float amplitude, float steepness, float2 direction, in float3 position, inout float3 result, inout float3 normal, inout float3 tangent, inout float3 bitangent)
{
// float L = waveLength; // wave crest to crest length in metres
// float A = amplitude; // amplitude - wave height (crest to trough)
float k = 2.0 * 3.1416 / waveLength; // wave length
float kA = k * amplitude;
float2 D = normalize(direction); // normalized direction
float2 K = D * k; // wave vector and magnitude (direction)
// peak/crest steepness high means steeper, but too much
// can cause the wave to become inside out at the top
// float Q = steepness; //max(steepness, 0.1);
// Original formula, however is more difficult to control speed
//float w = sqrt(9.82*k); // frequency (speed)
//float wt = w*Time;
float S = speed * 0.5; // Speed 1 =~ 2m/s so halve first
float w = S * k; // Phase/frequency
float wT = w * aTime;
// Unoptimized:
// float2 xz = position.xz - K/k*Q*A*sin(dot(K,position.xz)- wT);
// float y = A*cos(dot(K,position.xz)- wT);
// Calculate once instead of 4 times
float KPwT = dot(K, position.xz) - wT;
float S0 = sin(KPwT);
float C0 = cos(KPwT);
// Calculate the vertex offset along the X and Z axes
float2 xz = position.xz - D * steepness * amplitude * S0;
// Calculate the vertex offset along the Y (up/down) axis
float y = amplitude * C0;
// Calculate the tangent/bitangent/normal
// Bitangent
float3 B = float3(
1 - (steepness * D.x * D.x * kA * C0),
D.x * kA * S0,
-(steepness * D.x * D.y * kA * C0));
// Tangent
float3 T = float3(
-(steepness * D.x * D.y * kA * C0),
D.y * kA * S0,
1 - (steepness * D.y * D.y * kA * C0)
);
B = normalize(B);
T = normalize(T);
float3 N = cross(T, B);
// Append the results
result.xz += xz;
result.y += y;
normal += N;
tangent += T;
bitangent += B;
}