(C#) Issues implementing Gerstner waves

I’m having a few issues implementing Gerstner waves based on this GPU Gems article:

I’ve successfully implemented a 2D version of the algorithm, and I’m now trying to create a 3D version in Unity using C#. So far the code I have is:

void Update () {
    Mesh mesh = GetComponent<MeshFilter>().mesh;
    Vector3[] vertices = mesh.vertices;
  
    int i = 0;
    while(i < vertices.Length)
    {
        Vector3 newPos = new Vector3(vertices[i].x, 0, vertices[i].z);
        foreach (Wave wave in allWaves)
        {
            float inner = Vector2.Dot(wave.wavelength * wave.direction, new Vector2(vertices[i].x, vertices[i].z)) + wave.phase * Time.time;
            //Qi = Q/(wi * Ai * numWaves)
            float Qi = steepness / (wave.wavelength * wave.amplitude * waves.Length);
            float cosFunction = Mathf.Cos(inner);
            
            newPos.x += Qi * wave.amplitude * wave.direction.x * cosFunction;
            newPos.y += wave.amplitude * Mathf.Sin(inner);
            newPos.z += Qi * wave.amplitude * wave.direction.y * cosFunction;
        }
        
        vertices[i] = newPos;
        i++;
    }
    
    mesh.vertices = vertices;
    mesh.RecalculateBounds();
}

This is based on this equation in the article:

equation

Waves is a class containing parameters of each of the waves, eg amplitude, wavelength, and there are currently 4 waves with the same parameters.

This is the result:
1476ba5ab1fbe387b35fc02bc6a8388a-ezgif.com-video-to-gif-converter

Been trying to get this to work all day - any pointers as to where I’m going wrong would be really appreciated.

Well, the problem is that you modify the vertices and feed them back in the algorithm the next frame. This results in a gradual alignment of the vertices over the first few frames. You want to run the algorithm on the non modified original vertices.

In general you want to avoid reallocating the vertex array each frame. I suggest you create two vertices arrays in start. Use one as the source array and the other as the destination array.

Basically this:

    Mesh mesh;
    Vector3[] source;
    Vector3[] dest;
    
    void Start()
    {
        mesh = GetComponent<MeshFilter>().mesh;
        source = mesh.vertices;
        dest = new Vector3[source.Length];
    }
    
    void Update ()
    {
        for(int i = 0; i < vertices.Length; i++)
        {
            Vector3 newPos = new Vector3(source[i].x, 0, source[i].z);
            Vector2 dir = new Vector2(source[i].x, source[i].z);
            foreach (Wave wave in allWaves)
            {
                float inner = Vector2.Dot(wave.wavelength * wave.direction, dir) + wave.phase * Time.time;
                float Qi = steepness / (wave.wavelength * wave.amplitude * waves.Length);
                float cosFunction = Mathf.Cos(inner);
                
                newPos.x += Qi * wave.amplitude * wave.direction.x * cosFunction;
                newPos.y += wave.amplitude * Mathf.Sin(inner);
                newPos.z += Qi * wave.amplitude * wave.direction.y * cosFunction;
            }
            dest[i] = newPos;
        }
        mesh.vertices = dest;
        mesh.RecalculateBounds();
        mesh.RecalculateNormals();
    }

Works just fine for me.