Having explored using noise maps and Gerstner waves, we found each of those approaches to be lacking in some respect. It was time to delve into some more in-depth solutions in order to get the effect we were looking for.
In a paper title "Simulating Ocean Water" by Jerry Tessendorf, Tessendorf explains an approach where through a Fast Fourier Transform (FFT), many waves are layered together to create an effective simulation of an Ocean. So effective in fact, that the technique has been used in special effects for Hollywood movies, albeit not in realtime of course.
In essence, this is the combination of our previous attempts with Simplex noise and Gerstner Waves. The Simplex noise attempt took a random noise pattern and through fractional Brownian motion (fBm) we layered multiple passes at different frequencies to get an interesting result. Using Gerstner waves we attempted to individually model each wave as a unit producing a believable shape for a single wave. This FFT approach combines the two and layers multiple passes of waves at different frequencies which should ultimately get our Ocean simulation moving as we had hoped.
We'll simplify the process and concepts for the purpose of this post but if you're interested, we highly recommend you read through Tessendorf's paper to fully understand what's going on.
First, we first need to understand what FFT does. At a high level, it is a way to transform a set of data from one frame of reference to another. This is similar to how a transformation matrix can be used in 3D environments to transform a set of points from world space to local space.
FFT transforms the data from the time/space domain to frequency and vice versa. You'll see that explanation almost verbatim when you Google it but what does that really mean in the context of our Ocean?
Our Ocean is made of many many waves all added together. Ultimately, in order to create our mesh, we need to be able to sample the height at any 2D point in the world. For example, at X=104 and Z=56 what is our Y? It turns out that we can generate an image of the Ocean in the frequency domain and then by running it through the FFT, we get the data in the spacial domain. Once we have our spacial data, we can perform a quick look up and get the Y value which represents our height displacement.
Have we lost you yet? Don't worry there are lots of fancy moving pictures coming up!
We should note that generating this spacial data once is not enough to animate the ocean since it is just one frame. You'll need to generate this data each frame or precompute a loop and play through it.
We knew we weren't done yet but we decided to try and sample the height map before running it through the FFT. As you can see in the image below you get something that looks like this:
Something is happening but it's not very ocean-like. What is interesting to note is the very circular periodic motion of those two vertices on each end. This is visualizing the frequency of the waves which should be going up and down over time. This at least let us know we're on the right track.
Converting the data to the Spacial domain with the FFT gives us something much more familiar.
Now we have some motion in our Ocean! It looks watery, "feels" watery and it's complex in a way that would be nearly impossible to do with multiple hand tweaked Gerstner waves. So we have our Ocean simulation right? Not quite. There are still a few flaws. The most noticeable is that one of the edges is clamped and doesn't move. To solve that problem we needed to handle wrapping the lookup which was pretty trivial. The other more subtle issue is that each vertex is only moving up and down in a straight line. To fix that we needed to add offsets in the X and Z dimension as well.
Similar simulation, but this time with vertices moving up and down as well as compressing and stretching from their neighbours.
Not only does this approach look better, it's also easier to use. By tweaking a few simple parameters we could allow the simulation to handle different environmental conditions too. Like any developer with a new toy we started playing the parameters to see what we could get out of this approach.
We managed to get a pretty decent variety of Ocean conditions. In the image above, we have a calm ocean with just a few lazy waves rolling through. In the image below below we have a violent storm with a high frequency and amplitude of waves crashing into each other.
Satisfied with the versatility and believability of the FFT approach we we're ready to move on. While it took a little bit to get here, we finally have an approach that will satisfy our goals. There's still a lot of work to do, but we're on the right track and we have something that physically looks convincing. We'll count that as a win and move onward to the next step.