Oculus + Ocean Simulation = ???

With a little down time before the Holiday break I was looking for a quick and fun experiment to try out. Jon's ocean sim really is interesting but it's not really something I can contribute to at this point. Still in it's early stages, the ocean sim is a tangle of undocumented code as Jon works through different techniques for everything from wave generation to LOD and tiling. Unable to add to the system itself I started thinking, what would it feel like to be right down there at wave level? How tall do these waves really feel? Why haven't I hooked the Oculus up to this thing yet!

As I set out to be the first to virtually sail Jon's ocean I had a few personal goals in mind:

  • Familiarize myself with the Oculus SDK and it's application to an existing project
  • Gain a better understanding of how the ocean sim operates from the perspective of a developer using the system
  • Evaluate and provide feedback to Jon on values needed from the system from the perspective of a developer using the code in a real world situation
  • Explore how to create convincing buoyancy with relatively little effort and acceptable performance

With a little hand holding from Jon I was able to get a copy of his ocean sim project up and running in about an hour. From there I branched the code, quickly found a boat model on the Asset Store, and was good to go!

Free low-poly wooden boat from the Unity Asset Store https://www.assetstore.unity3d.com/en/#!/content/780

Free low-poly wooden boat from the Unity Asset Store https://www.assetstore.unity3d.com/en/#!/content/780

Keeping My Head Above Water

Alright, so the first order of business was to actually collide with this water. Inspecting the scene at runtime revealed that Jon's ocean is a series of large meshes representing different levels of detail as they get further away from the camera.

Since we're just dealing with a mesh that get's modified at runtime the easiest solution to try first was to add a MeshCollider to the Ocean and a Box collider to the boat. For the most part this worked well but there were a few hiccups along the way.

For one, the MeshCollider is not designed to work on a mesh that is constantly changing and it really shouldn't be used for this purpose. The result is that the collider takes the shape of the mesh at frame one and never updates. However, it is possible to work around this limitation and force the collider to update by reassigning the shared mesh property every frame. Code sample below:

void LateUpdate()
{
    //Set the shared Mesh to null first to force the collider to update  
    m_MeshCollider.sharedMesh = null;
    m_MeshCollider.sharedMesh = m_Mesh;
}

The issue I ran into was that the BoxCollider on the boat didn't behave as I had predicted. The large box tended to average out the peaks and valleys of the water and generally prevented the boat from rolling side to side. To fix this issue, I replaced the BoxCollider on the boat with four SphereColliders positioned at each corner of the model.

Four sphere colliders positioned roughly at the corners of the boat

Four sphere colliders positioned roughly at the corners of the boat

This allowed the corners of the boat to rise and dip with the movement of the water. The down side was that now it was possible for the water mesh to come up through the bottom of the boat in some situations. I'm sure this could be solved with some clever culling or changes to the render order but it was outside the scope of this one day experiment and certainly wouldn't prevent you from feeling sea sick!

While not the most performant solution, it was easy to implement and suitable for the purposes of this experiment. In an actual game we would want to calculate the wave height at just the position of the boat each frame rather than at all vertices across the visible ocean as the MeshCollider does.

 

Playing Around with Wave Resolution and Speed

I had a good start but the current configuration was lacking large waves and looked like a calm day at sea. Consulting with Jon again he directed me towards a few key parameters to start tweaking to get the effect I wanted:

  • Tessendorf Simulation Time Step - Reducing the length of the time step for the wave data generated would effectively slow down the movement of the waves. This is because the simulation time steps are independent of the actual playback speed of the data.
  • Mesh Displacement Vector - This parameter controls the magnitude of the wave data representation. At it's core the wave data generates values between -1.0 and 1.0 and it is up to the renderer itself to dictate how exaggerated that data is. This parameter would help me control the scale of the waves relative to the boat
  • Wave Resolution - Increasing this parameter increases the resolution of the mesh at the cost of performance. Increased mesh resolution helps give the waves more detail rather than just representing the largest wave movements.

After spending a couple hours tweaking and testing the different values I was happy with the result. Ultimately, I ended up halving the speed of the waves, increasing the wave displacement by 33% and doubling the resolution. With the resolution in particular I had lots of processing power to spare since Jon has been designing this sim to work on mobile devices and the Oculus experiment was a desktop build.

Comparing the calm waves we started with to the resulting rough waves.

Comparing the calm waves we started with to the resulting rough waves.

Getting that Sinking Feeling

With the boat resting atop the water and the waves dialed in it was time to attach the camera to the boat and throw on the Oculus to see how these waves felt.

Getting bumped around by hard collisions with the waves.

Getting bumped around by hard collisions with the waves.

As you might gather from the clip above the experience was, well, disorienting. With the boat abruptly pitching in every direction it was less like bobbing in the ocean and more like flying across some terrifying toboggan run. The effect of the ocean on the boat needed to be dampened so that it would behave more as it would in the real world.

To add dampening, I thought the simplest approach would be to detatch the sphere colliders from the boat and attach springs between the boat and the colliders. Using the spring tension I could then, in effect, control the buoyancy of the boat. After spending more time than I care to admit trying to get the springs to work the way I wanted I scrapped that idea and went searching for a quick solution on the forums.

As luck would have it I found a surprisingly effective buoyancy script written by alexzzzz. After a quick modification to have the script sample the correct ocean height rather than assuming a flat surface at y=0 I had a pleasantly bobbing boat being tossed around.

bobbingDebug.gif

Sea Sickness Achieved

Now that the effects of the ocean were dampened a bit it was time to try on the Oculus again. This time the feeling was much more convincing. WIthin a few minutes I had to take the headset off but interestingly enough it wasn't the big waves that did me in. With the larger waves it was easier for my inner ear to convince my eyes that the motion wasn't real but with the slower more subtle motions my senses were convinced that the motion was real.

 

Adding Some Decorations

Since the ocean was feeling good and this experiment was going well I decided to add a few visual touches to the demo to make it a little more approachable to someone who wasn't used to looking at dev art.

FIrst, I added some basic controls to the boat by mapping the left, right, forward and back arrow keys to the boats movement. The movement is by no means accurate but again it serves its purpose for this demo.

Next, I added a simple skybox to replace the bland blue background. After adding the skybox the simulation felt much more disorienting. The boat's movement felt much quicker with the new points of reference in the sky for my eyes to track.

Adding sky using free sky pack on Unity Asset Store https://www.assetstore.unity3d.com/en/#!/content/24923

Adding sky using free sky pack on Unity Asset Store https://www.assetstore.unity3d.com/en/#!/content/24923

Finally, I swapped out the debug water material with something a little more convincing. This made it harder to see the definition of individual waves so I added a little transparency to allow you to see the  edges more easily. All said, the results were half decent considering how quickly the water material was slapped together.

Final result with the updated water texture and skybox

Final result with the updated water texture and skybox

Download

 

Windows:
Oculus | Non-Oculus

 

Disclaimer: This is experimental software, it may crash, make you sick, or cause any number of other unintended side effects. Run at your own risk. See the Readme.txt for a list of controls.

 

Conclusions

The Oculus SDK is really easy to integrate with Unity. It was simply a matter of installing the Unity Package, dragging the camera rig prefab into the scene and treating it like a normal camera from there on out. I did run into an issue where the camera was facing backwards in the oculus but I think that was an issue on my end rather than an issue with the Oculus SDK itself.

Applying MeshColliders to the waves performed much better than expected. Ultimately, the MeshColliders were removed since the buoyancy script was able to directly sample the wave data but the MeshColliders being updated every frame performed better than I expected. Maybe I'm just too accustomed to mobile performance limitations...

Regarding the ocean system itself we learned a lot about real world use cases. Jon's put together a great foundation to build off of in the future but we both agree the exposed parameters at the moment aren't very intuitive. This test run has provided a lot of insight into the direction the API will need to take as Jon continues to build upon the ocean sim.

Considering only a day and a half of effort was put into this experiment the visuals don't look half bad. That said, the visuals aren't anywhere near where we'd like them to be for an end product and we have a lot of work to do on that front. The benefit of an experiment like this is it forces you to really think through all of the visuals you'd like to have. White caps, animated surface textures and wakes were already on the TODO list. This experiment forced us to think about the need for masked rendering to keep the water out of the boat and particle spray when the waves crash into solid surfaces.

Surprisingly, through testing I found smaller wave movements to be more uncomfortable than the larger waves. I suppose it's not surprising that the smaller waves had more of an effect on me than the larger waves. With experiences like Cyber Space I find the most uncomfortable part of the experience the first few slow swings. I'm really curious to see how other people people react and what their feelings are. If you do download and try out the demo leave a comment or send me an email and let me know how you felt!