Before I get into the displacement functionality of the wave tool, I’d like to take a little time to go through some foundation work I did to improve displacements.
Up till now I was using regular noise functions to create the displacement of the ocean surface. This looks kind of ok as long as you don’t look to closely at it. The problem with regular noise functions is that the wave forms they generate have similar roundness at the top and the trough of the waves. Real waves, however tend to be ‘sharper’ at their tops and rounder at the troughs. So I thought, I’d better fix that before I continue with the displacement functionality of the wave tool.
While looking at ways to achieve more realistic wave shapes you quickly find there are a few really clearly explained and often used formula to generate wave displacements: Gerstner waves, Stochastic Wave models based on FFT’s, … I already new that the team at Sony Pictures Imageworks had based their wave train generator on the Gerstner model and since FFT’s are not quite my cup of tea I figured I’d read up on Gerstner. I quickly found a very helpful paper: Simulating Ocean Water, I later also found a paper which describes a real time system that uses the same formula: Interactive Animation of Ocean Waves. Especially the fact that the system described in the later paper can reach real time performance on modest hardware looked promising.
Now in case you might be wondering, why I don’t just use HOT (Houdini Ocean Toolkit)… I know this toolkit has been used to create some really great ocean scene’s for big films. The only thing I don’t like about it is that it requires CVEX (Houdini’s C++ based API) code compiled to a specific build of Houdini. As I typically follow the daily builds of Houdini typically once a week this kinds of works against me. In addition to this its always good to get into a level of detail to really understand what makes a thing tick, so this is another reason for building this part of the functionality myself.
So after figuring out what Gerstner’s formula were all about, I set out to encapsulate the formula in VEX code. I took a side step in this to figure out if I could dynamically generate a texture which I could then use both for displacement and visualisation. This approach would solve a visualisation problem I have with the rough area on the lip of the wave. Currently, it is only possible to visualise the rough area directly in the view port, but not the foam that is generated from this. In order to see the foam you currently have to turn on ‘Visualize Lip Roughness’ and then render, which will render the lip foam. If I could generate a texture dynamically within the compositing context of Houdini, I could directly show this texture on the lip surface in the view port. Another way of achieving this is to write an OpenGL shader which is a bit too much for me at this point. However, it turned out its fairly easy to generate the texture by creating two Vex Compositing Filters. This approach opens a lot of very interesting possibilities. It does however quickly eat a lot of memory. I’ll get back to this in a later post when I get into the displacement part of my wave tool.
After this side step, I reworked the VEX code into a separate VOP. This makes it possible to use the same tool in geo displacement, displacement shaders and the compositing scenario. The code itself turned out to be very simple and fairly compact. It also ends up being fairly fast. It’s fast enough to displace a 800×800 grid interactively in the view port, which means its possible to have fairly high res previews while tuning the look of the displacement, which is really nice.
The parameters to control the wave surface and animation are fairly simple.
The Wave vector is a vector that specifies the direction in which the waves will travel.
The Amplitude speaks for itself.
The WaveLength specifies the length of a single wave. For realistic waves the wave length has be quite a lot bigger than the amplitude (as much as 30 times the amplitude or even more). The amplitude and wave length together determine how steep the top of the wave will be. The smaller the wave length gets in relation to the amplitude the sharper the tip of the waves will be. At some point this goes wrong and the wave tip turns inside out. The wave length also determines the speed at which the wave travels (longer waves travel faster).
The surface size is important when the position data that is processed by the VOP is in UV space (0-1) and is used to specify the dimensions of the displaced surface in object space as the Gerstner formula operate more or less is real scale (e.g. it includes gravity).
The choppyness factor is a parameter I added to the calculations to add some more creative control to the waves. A value above 1 will increase the steepness (or choppyness of the waves) a value below will reduce it.
The octaves parameter describes how many octaves will be calculated (multiple waves of decreasing size).
The Wave length gain and Amplitude gain specify a factor with which the wave length and amplitude will be reduced (or increased) each following octave. I find that it helps to keep the amplitude gain lower than the length gain, but that might be a matter of taste (and I haven’t really gotten the change to play with my new little toy that much).
The Angle spread and Angle seed control a random change in wave direction for each octave. The spread defines the maxim deviation in angle (both positive and negative). The seed controls the random function and can be used to search for a solution that looks good.
These are my initial go at the UI for this new tool, so I might change these a lot or not at all. I’m not sure about the random wave direction variation for the different octaves. It works but sometimes it takes a little time finding a seed which gives nice results. I might end up adding a fixed number of angle parameters with which the angles for a number of octaves can be specified explicitly. Note however that its possible to combine multiple DEFWaveDisp VOPs if more control is wanted. This can be particularly handy to have a large, medium and small set of waves with their parameters, which gives more control than just having a lot of octaves.
As the basic Gerstner formula don’t generate very much variation in the shapes of the waves, I added some noise parameters that can break up the repetitive nature of the formula’s. It’s possible to use a noise field to break up the position, the amplitude and the choppyness of the waves.
Here are some renders of some quickly slapped together wave setups:
This first test is a very simple setup with one DEFWaveDisp vop displacing a grid of size 400×400 with 600 rows and columns and with an amplitude of 0.6, wave length of 30, 3 octaves and an angle spread of 45. I’ve used some noise fields to break up the regularities.
This second test shows much bigger sea’s. In this case I’ve combined two displacements. The main displacement has an amplitude of 2.5, wave length of 60 and 3 octaves. The second displacement has an amplitude of 0.3, wave length of 14 and 4 octaves.
Obviously these tests are very simple and rough. To be honest, I just finished this little tool and haven’t really had much time to play with it and find out myself how to best use it. So far I’m quite certain though that this will be a good enough solution to generate the wave trains for displacement in my wave tool. At first I’ll be using it to add small waves on the surface of the ocean and the breaking waves. However, for the longer term, I have some idea’s on how to integrate the breaking wave and this procedural wave solution where waves are first based on this tool and can be morphed into a breaking wave strip at some point during the animation. This would allow the waves deeper in the ocean to be generated using this and only where the waves come towards the shore the more complicated but also more animatable tool takes over. But as this is still very much an internal project, we’ll see if and when I get to that direction…
For those interested in the actual code:
Now I think, I’m fairly well positioned to replace the current displacement shader in my wave tool with this new gerstner wave based one. So that will be the subject of the next post (probably ;)).