I have been learning to create water waves in a mesh from a paper commonly known as the Tessendorf paper. Which is quite well known.

But those who don't know this is the equations it mentions in the paper:

enter image description here

The paper is here (and is freely available, it is not protected): http://citeseerx.ist.psu.edu/viewdoc/download?doi=

Page 4 right column for this equation.

My code for this is in C#:

public static void Calculate(List<WaveData> waves, Vector3[] vertices, List<Vector3> result, float time)
    for (int v = 0; v < vertices.Length; v++)
        Vector2 pos = new Vector2(vertices[v].x,vertices[v].z);
        Vector2 val = Vector2.zero;
        float y = 0;
        for (int i = 0; i < waves.Count; i++)
            val += waves[i].CalculateXZ(pos, time);
            y += waves[i].CalculateY(pos, time);
        var temp = new Vector3(pos.x-val.x,y,pos.y-val.y);
        result[v] = temp;

Wave data struct:

public struct WaveData

    public float Amplitude;
    public float Frequency;
    public Vector2 WaveDirection;
    public float Phase;
    public float WaveLength;

    private Vector2 _waveVector => WaveDirection * (2 * Mathf.PI / WaveLength);
    private float freq => Mathf.Sqrt(9.81f * _waveVector.magnitude);

    public Vector2 CalculateXZ(Vector2 pos, float time)
        return WaveDirection * Amplitude * Mathf.Sin( Vector2.Dot(_waveVector, pos) - freq * time + Phase );

    public float CalculateY(Vector2 pos, float time)
        return Amplitude * Mathf.Cos(Vector2.Dot(_waveVector, pos) - freq * time + Phase);

I apply this algorithm to my flat mesh: enter image description here

The issue is when you have more than one wave in the summation (below image is 2 waves) the final result shows the mesh penetrating through itself at the crests of the waves, and its nearly impossible to have more than one gerstner wave before this happens, i've tried many values and its rarely every not doing it:

enter image description here

Is there any mistakes in my interpretation of the algorithm, i don't know why it keeps happening !? The colour just represents the direction of the normals.

  • Shouldn't CalculateXZ have this k/k division with k = 2*pi/WaveLength? The horizontal displacement might be to big. Feb 27, 2019 at 22:23
  • I can't tell if its saying the k should be normalized to have a magnitude of 1 before dividing by (2*pi/WaveLength). The issue im finding is the higher the amplitude the less wavelength i can get and vice versa. Making it very difficult to represent water waves even with just one wave.
    – WDUK
    Feb 27, 2019 at 22:59

1 Answer 1


See the discussion here in GPU Gems (scroll down a bit to the Gerstner Waves section).

The relevant bit is this:

When the sum Qi x wi x Ai is greater than 1, the z component of our normal can go negative at the peaks, as our wave loops over itself. As long as we select our Qi such that this sum is always less than or equal to 1, we will form sharp peaks but never loops.

They formulate the waves this way:

GPU Gems

Note that all the expressions on the right are for components, so the "⨯" is just ordinary multiplication, while the "•" is the dot product.

Also "P(x, y, t)" doesn't name the components, just expresses that P is a function of x, y & t (plane vertices and time); I'll denote the components of P using P = [P.x, P.y, P.z]. In GPU Gems, the z-axis is vertical (in the Tessendorf paper, y is vertical).

Comparing that to what you have, in GPU Gems notation, x & y are the two components of Tessendorf's x0 vector, while GPU Gems P.z is Tessendorf's y-displacement, with the sin and cos switched up (this doesn't matter) and some signs changed (this just changes the direction).


So, with some guesswork, we can write down a translation table of sorts between the two (Tessendorf's notation on the left), for the relevant inputs/parameters:

x0.x ---> x
x0.z ---> y
x0 ---> (x, y)
k[i] ---> w[i] * D[i],

... where D[i] is (I think) a unit vector, implying that k[i] ---> w[i] (the magnitude of k), that is:
k[i]/k[i] ---> D[i]


A[i] * k[i]/k[i] ---> Q[i]A[i] * D[i]
A[i] ---> Q[i]A[i]

So, if I'm not mistaken, GPU Gems' (rearranged) expression

sum(Qi * Ai * wi) <= 1

should translate to

sum(A[i] * k[i]) <= 1, where k[i] is the magnitude of k[i].

Note that with the introduction of Qi in GPU Gems, what they are really doing is using a different amplitude for the horizontal displacement that they can control separately (wheres the Tessendorf paper uses the same for both horizontal and vertical); i.e. Qi doesn't appear in their P.z - so you may want to try something similar. Intuitively, the Qi parameter "controls the steepness of the waves":

In fact, we can leave the specification of Q as a "steepness" parameter for the production artist, allowing a range of 0 to 1, and using Qi = Q/(wi Ai x numWaves) to vary from totally smooth waves to the sharpest waves we can produce.

So you can have an overall Q, and set up each Qi in such a way that the overall sum is:

Qi = Q/(Ai * wi * N)
sum(Ai * wi * Qi) = sum(Ai * wi * Q/(Ai * wi * N)) = "sum N times"(Q/N) = Q

BTW, I haven't tried/tested this, so while it's possible that I've overlooked something, hopefully it's good enough to let you figure it out.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.