Every now and then I run into a feature in a project that I can’t help but become totally obsessed with – it’s a bit like a trip to Best Buy or Walmart when the original intent is to buy a phone charger. Half an hour later, your eyes are about as close to the silicon on the new GPU in the electronics section as possible – all the while, your wallet let’s out a puff of smoke reminding you that you’d rather eat this week. While the diehard project manager and realist in me says, “No, no, no, that’s not on the TODO list, that’s good ole fashion scope creep!”, the tech-obsessed junky in me let’s out a rebellious cry, “I don’t care it looks cool!” And in most cases, this is, generally speaking, an unproductive tangent in terms of achieving the original goal. However, in some cases, the time I spend oogling at some new tech, a particular implementation of an algorithm or a new design philosophy actually works to benefit the greater project. And maybe I’m being a bit too black and white, because the reality is, there has yet to be a project that I’m slightly interested in that I haven’t found something that sparks some passion and takes me for an unexpected journey. So, maybe, the two are inextricably connected – it’s difficult for me to truly engage with a project without – in some capacity – getting sucked into a feature that may or may not see the light of day.
This brings us to today’s random, unbelievably time-consuming tangent: fluid simulation. We regret to interrupt your regularly scheduled, Trello-board-approved application development to introduce the world of Navier-Stokes and computational fluid dynamics. Yes, I love simulations. Especially ones where I can really go, “Hm… that actually looks like “insert the real thing here”.” I’ve dabbled with weather simulations and particle simulations, so I figure fluid is next logical step. To put things in a bit of context before I begin my long-winded spiral into drowning the reader with information about fluid simulations (drowning… see what I – I’ll see myself out), this fork in the road originated from my team’s choice of simulation for the Simulation Challenge project for the CS467 Capstone Project.
Out of all of the possible simulations out there, we picked Virtual Bartender. Why? Well, I’m not entirely sure but it sounds pretty fun! I’m used to simulating really, really small stuff or really, really big stuff which isn’t very exciting unless you spent 90% of your childhood building HotWheels tracks. So, the Virtual Bartender gig sounded neat, a nice change of pace and in terms of scope, seemed the most reasonable given the allotted time and our team’s experience using the necessary tools. The tool we’ll be using the most is none other than the Unity game engine. In terms of the simulation itself, the goal is simple – make drinks using tactile, mechanical movements in a VR environment. While this is unbelievably vague, this is as far as we’ve made it so far. We’ve committed ourselves to restricting our focus to ensuring the main mechanic of this simulation, making the dang drinks, feel wonderful. After that, we can choose if the theme is “Space Bar”, or “Under-Sea Atlantis Bar”… or Canada or something… That being said, I don’t have a lot of info regarding the goal of our project, which isn’t a terrible thing at this point in time. We’ve fleshed out the general requirements pertaining to performance, platform/device agnostic API and a general algorithm from which we’ll work to establish the core simulation/gameplay loop. In other words, we have our sand in a truck which we’ll use to build our sand castle – it’s still in the truck, though.
Back to liquids and stuff! You may be two steps ahead of me here, but humor me. We’ve got a Virtual Bar, simulating the experience of a bartender. Now, I’m no bartender and I don’t know the first thing about casual conversation under a dimly lit, stained-glass light fixture from the 80’s while gently swirling a gin and tonic, but I do have a sneaking suspicion there is quite a lot of liquid involved. That being said, how does one simulate liquid? What even is liquid?
Well, to keep things simple, for our purposes, fluid is the volume that defines an infinitesimally small collection of particles that are in constant interaction with one another. And to keep things even simpler so our poor outdated GPUs don’t vomit, maybe we scale back the “infinitesimally” small to “reasonably” small and we partition these particles into their own little cells. Each cell is responsible for keeping track of a bunch of things. Namely, velocity and density. To be even more specific, the cell needs to keep a “memory” of the velocities and densities that it has already recorded. That way, the cell has some knowledge about how it may interact with its neighbors. However, before we get too deep into the meat and potatoes of the core algorithm, it’s worth noting that are a few assumptions we need to make about our fluid. The first is that it’s incompressible. This simply means that the amount of fluid that enters the space stays the same throughout the entire simulation. In other words, conservation of mass. The next is that the means by which the velocities that dictate the circulation of density within the volume is performed via a few processes: diffusion, projection and advection.
Diffusion is the means by which the fluids moves relative to the fluids viscosity, mass of the particles that make up the fluid and temperate (just to name a few variables here).
Projection is the process by which we maintain that first assumption, the incompressible nature of the fluid. After we do some work on the velocities that more or less define the rate of diffusion of the liquid, we need to make sure that we didn’t actually lose any liquid in the process.
Finally, Advection is the means by which the fluid particles themselves move due to the bulk motion of the fluid.
So there seems to be a bit of a niche distinction between Diffusion and Advection, right? Well, the most important difference is that Diffusion refers to a transport mechanism of the fluid that which occurs without any motion of the bulk fluid and Advection refers to the transport mechanism of the fluid due to the motion of the bulk fluid. Still a bit confusing, right? A simple example of Diffusion is the transfer of heat and energy between particles in a drink. A simple example of Advection is the transfer of heat and energy of the particles in the atmosphere as the general circulation of the Earth and regional movements of air around high and low areas of pressure. Diffusion: small. Advection: big.
Back to the general process of simulating fluid: Diffuse the velocities of each cell of the fluid in all axes using the viscosity as the leading factor of change. Perform projection to ensure that the velocities aren’t unreasonable values and that the fluid stays within it’s bounds. Perform advection on each of the cells in all axes using the diffused velocity values as a delta. Again, perform projection to make sure we haven’t broken anything. Finally, we need to diffuse and perform advection on any differing densities – meaning, if we placed dye in water, the dye is the differing density. This final stage is essentially what creates disturbances in the fluid, creating noticeable changes of density throughout the volume.
This was a lot and I’m not quite to the point of implementing this, as there’s just enough math to keep me busy. However, I hope to have a working prototype soon enough that it doesn’t interfere with the development of our Virtual Bar! Worst case scenario, the fluid simulation will never see the light of day and I have a bunch of fun learning about it.