Jump to content
  • Advertisement
Sign in to follow this  

Understanding Gerstner Wave sharpness and normal calculation

Recommended Posts

Hi, I am currently working on implementing Gerstner Waves into a Unity shader based on this article.

Note: All of my code is in C#, running on the CPU. I did this since it allows for much easier debugging/code inspection. After the algorithm is complete, I intend to rewrite it on the GPU in HLSL.

I got this far implementing the positional part and started working on the normal part: https://imgur.com/SxSfOHE

The calculated normals did not turn out so good. The following is a gif where the first half is the normals being calculated using Equation 12 in the article. The second half is using Unity's Mesh.RecalculateNormals(); method, which manually calculates the normals of each vertex by summing the weights of adjacent triangles (which I have been using to test the correctness of the algorithm, not that they would be exactly the same).


I realized my normals were having issues due to the wave sharpness (Q parameter in the article) being too high. Initially, I had a wave sharpness parameter for each individual wave, whereas reading the article again I realized it was a global parameter calculated for each wave as Qi = Q/(wi Ai x numWaves). I changed my sharpness to be a global value from 0-1 and have this code currently.

void GerstnerWave(Vector3 i, out Vector3 p, out Vector3 n)
        p = new Vector3(i.x, i.y, 0);
	    foreach (Wave wave in waves)
            float innerFunction = Vector2.Dot(wave.wavelength * wave.direction, new Vector2(i.x, i.y)) + wave.phase * Time.time;
            float cos = Mathf.Cos(innerFunction);
	        float Qi = globalSteepness / (wave.wavelength * wave.amplitude * waves.Length);
	        p.x += (Qi * wave.amplitude) * wave.direction.x * cos;
            p.y += (Qi * wave.amplitude) * wave.direction.y * cos;
            p.z += wave.amplitude * Mathf.Sin(innerFunction);
	        n = Vector3.zero;
	        foreach (Wave wave in waves)
            float wa = wave.wavelength * wave.amplitude;
            float s = Mathf.Sin(wave.wavelength * Vector2.Dot(wave.direction, p) + wave.phase * Time.time);
            float c = Mathf.Cos(wave.wavelength * Vector2.Dot(wave.direction, p) + wave.phase * Time.time);
	        float Qi = globalSteepness / (wave.wavelength * wave.amplitude * waves.Length);
	        n.x += wave.direction.x * wa * c;
            n.y += wave.direction.y * wa * c;
            n.z += Qi * wa * s;
	        n.x *= -1;
        n.y *= -1;
        n.z = 1 - n.z;


Where is the input vertex position, the output position and the output normal. Wave is a class that holds all the wave specific values. Here is what I have using this: https://imgur.com/NaR5zXB

All of the gifs so far are 5 waves with the same setting, except for the previous which has a global steepness value of 0.95. This looks a bit rough right now. I can't tell if the normals are wrong or just so many waves layered are naturally creating noise (Unity's RecalculateNormals is still much smoother). Not only that, but without individual steepness values the waves are pretty round and dull looking. To investigate, I created a simple wave system on a 10x10 plane. 4 waves moving vertically, horizontally and both diagonals. These normals turned out fine (look almost the same as the Recalculated ones with any steepness 0-1).


So couple questions:

  • Is the method of calculating normals above robust for 5+ variable sized waves. Am I doing something wrong?
  • Having a global steepness value that is clamped from 0-1 makes the normals look correct in simple systems, but makes it difficult to get the waves to a higher sharpness value I need for the scene. Is it possible to have individual wave sharpnesses and get correct normals? I mostly just want my first gif, but with proper normals.

Sorry for the massive post but have been working on this all weekend and want to figure it out! Thanks for any help,


Edit: Not sure why code formatting is odd, updating doesn't seem to fix it :(


Edited by Iron-Warrior

Share this post

Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

  • Advertisement

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!