Fluid Simulation Theory

These are personal notes I wrote while exploring fire simulations and how they work in computer graphics. This is not a rigorous mathematical explanation of it by any means, but hopes to provide some intuition when working with general fluid simulations.

This assumes a basic understanding of differential and vector calculus, but I may get too excited and pull from this expectation.

Hope you find this interesting!


To begin with, you might read up on volumes in computer graphics and hear them described as voxel grids, voxel fields, etc. They also can come in a data format called VDBs, which is a compressed file format and comes with a bunch of algorithms to manipulate volumes. The details aren't important right now.

What is a volume and why are they useful?

You can think of it like a 2D plane of pixels from a flat image, except in 3 dimensions. So you have 3D voxels in 3D space.

Volumes can hold useful information (below drawings will be in 2D for simplicity).

They can either represent a scalar field, or a vector field.

A scalar field is when each voxel holds a single number value (float).

A vector field is when each voxel holds vector information of n dimensions (we will be mostly using 3-vectors for 3D space).

For example, if we wanted to represent something like smoke, or a cloud, you could make use of a scalar field, where each voxel represents the density of the smoke/cloud. Then you can use a volume shader to make it look all fluffy. You just need a sufficiently high resolution.

How else could it be used?

What if you want to move some smoke around? Then you could, say, give it a velocity field in combination with its density field to push around the smoke.

It's very important for mastering fluid simulations! This includes both water and fire/gases, as they both have freeform shape. In Maya Bifrost graph editor, here are some properties that you can observe from the default fire solver.

Notice how under the data column, we have these voxel fields! We have arrays of 3D vectors, and arrays of scalars. Combustion models to simulate fire require a lot of vector and scalar fields.

Luckily, Maya does most of the heavy lifting for us. It calculates the F = ma of every single voxel to calculate all of these properties to see how the fluid evolves over time. And all the properties that are involved in the simulation are useful for shading these volumes.


Here is a side note on the math and science behind mathematically modeling simulations using volumes. In the context of our problem, how fire evolves over time. I don’t have a masterful understanding as much as someone who codes these systems from scratch, but I will try to explain how fluid simulations in 3D work with the framework we have so far. The minimum one could know to gain an intuition on art directing these simulations.

First, let’s define what a fluid is.

A fluid is something that has freeform shape and yields to external pressure (thanks, Google dictionary!). In terms of fire, it involves a chemical reaction called combustion. Combustion has two reactants, any sort of fuel that has carbon and oxygen. The products are carbon dioxide, water, and other stuff that we don’t really care about.

If you’ve taken any chemistry course, then you may have heard of covalent bonds and how molecules want to share electrons! Well, carbon and oxygen molecules want to snap together, which causes a massive chain reaction of releasing of energy which causes fire! But then, why doesn’t oxygen in the air cause a fire all the time? That is because carbon and oxygen molecules don’t know that they fall in love once they get close. They have an initial repulsion due an initial repulsion because of their electron clouds. So they need a little push to overcome this period of romantic coldness between the two. And how do we quantify this? Temperature! Because it is a measure of average kinetic energy. Once there is sufficient temperature, and there is sufficient oxygen diffusion, a fire finally starts.

In a fire simulation, we are working with fluids, since we work with gases. You can see that Maya Bifrost graph involves scalar fields that define the density of different reactants and products. It is cool that it exposes all these parameters to you, you could even play around with them yourself to get interesting behavior.

We use differential equations to model how physical systems evolve with respect to time. Differential equations relate multiple functions and their derivatives. In the natural language of motion, this is often a rewriting of F = ma.

In terms of fluids, we use a set of partial differential equations called the Navier-Stokes equations. Yes, reading up on them will cause a lot of hair pulling and cause you to bleed from your eyes. I’m sad I’ve never studied it in all my years of physics, it’s something all my mechanical and bio engineer friends have dealt with.

But, in general, all simulations evolve in the same way in computer graphics. First, there are few things you need to understand.

In computer graphics, we are working with discrete data when it comes to complicated simulations. This matters, because sometimes we can represent a lot of relationships in terms of functions, which leads to analytical, or exact solutions given a continuous parameter space. This is how we can parametrically define curves and shapes using equations such that they have an infinite resolution (i.e. vector graphics in Adobe Illustrator). To give some context, imagine a vectorized curve parametrized from t = 0 at the beginning of the curve and t = 1 at the end of the curve. Because it works in a continuous sense, we have access to an infinite number of values. 0.1, 0.01, 0.0000000000001… you get the picture.

If you have taken a course in differential equations before, then you’ll know that most differential equations have no known solution. But most things in life can be modeled with a differential equation. If you want any practical use of these equations, you will have to solve them numerically, or by manually calculating the derivatives via approximations instead of through exact calculation.

In differential calculus you take the limit definition of a derivative using forward, central, or backwards difference.

Forward difference definition to refresh your memory.

So let’s re-examine a general form of the Navier-Stokes equations. If you want a more detailed explanation of what’s going on here, I will link extra sources below. But I will describe what a computer does to solve this equation.

The computer stores all the necessary information for everything in this equation except for the flow velocity, or the vector valued function v and how it changes with respect to time. In the case of a film, it works within the time increment of each frame.

Everything is a function of the attributes stored in each voxel in our voxel field. If they aren’t directly exposed to us as the artists, they are hidden and calculated under the scenes.

If you are unfamiliar with the Del operator, or anything with the fancy triangle, is that the different notations of its use calculate different quantities with different geometric interpretations, but they all decompose into using spatial derivatives with respect to other scalar or vector fields. For example, instead of dy/dt, we use dy/dx, or any other spatial coordinate (x, y, z, r, theta, rho, phi, whatever coordinate systems suit your fancy to the context of your problem).

In our case, we try and approximate how a field changes with an infinitesimal change in space. And the fact that we are working with discrete data is precisely why we use the limit definition of a derivative! To check the spatial derivative of an attribute at a single point in space, we sample the value of that attribute and/or its neighboring voxels along a coordinate axis to calculate our limit definition of a derivative. Then, you can calculate almost any differential equation given relevant attributes.

In the equation I showed above, it is composed of three different terms:

-Del(P) measures a vector at each point of space which points in the direction of the rate of greatest decrease of the Pressure field. In mathematical terms, we are calculating the negative of the gradient of the pressure field. Intuitively, this should make sense, because when you think of a gas in space, gas tends to diffuse into areas of lower pressure.

Del . T is defined as the divergence of stress. This is used to describe horizontal friction and shear stresses. You can imagine how movement of a fluid in a local point of space will influence infinitesimal pieces of fluid nearby.

f describes external forces. This involves stuff like gravity, buoyancy, etc. Gravity and buoyancy are two examples of relevant forces in combustion simulations, I will cover how buoyancy works later in this article.

All three of these terms are vector functions, they all sum together to describe the rate of change of the flow velocity.

Of course, the accuracy and speed of your simulation depends on multiple factors. For example, the resolution of your voxel field. Also, your choice of numerical method. These are choices you can make as an artist, and for the most part, we aren’t truly concerned about complete accuracy. There are diminishing returns to the visual believability of a simulation vs. the exponential calculation time of a high resolution simulation.

One last thing I wanted to mention is that many physics solvers depend on real world units. In terms of spatial measurements, in meters. Many times have I learned through failure (and surely you would too, if you haven’t run a bunch of simulations wondering why they are acting strange) that if you are not working in real world units, you get a poor simulation. This is by and large the easiest overlooked mistake as a VFX artist.


Temperature is one important attribute for fire simulations, and not only is it a condition for starting a fire like I mentioned above. In the case of fire which works with gaseous products, these gases emit visible light when heated to a sufficient temperature. The emission of light varies in frequencies/color depending on their temperature via blackbody radiation. This is the mechanism for why fires emit light!

Temperature also is an important factor in various forces in nature. Buoyancy is one such force relevant to fluid simulations, where the rate at which gases rise increases with temperature. This is because density of a gas decreases when heated. Less dense fluids rise above more dense fluids! This concept combined with gravity and atmospheric pressure pushing cooler, denser gases down gives rise to the characteristic teardrop shape of a flame on Earth.

But what's really cool as an artist, is that we can hijack every single one of these attributes given to us, not just temperature.

For example, we can add in our own fields and combine them with the fields of the solver.

Say we wanted a swirly fire that rotates about the center, like a tornado. Here is a quick way to set up a vortex field to add to the velocity. Imagine the following:

Then you can just play with it and distort it however you like.

Do you want the rotation to be stronger on the inside, weaker on outside?

Scale it with the inverse of the magnitude of P. Can have it nonlinear by using some other function, like an exponential or any other useful shaping function.

Another way to add variety is to add noise. Noise is the catch all term for 2D/3D textures to try and create variety. Some kinds of noise are very smooth, while others are very jagged, and they all have parameters you can use to stretch its effect.

Here is an example of a type of vector field noise called curl noise:

Curl noise visualized using vector trails in Houdini.

One neat thing about curl noise is that it is divergence free. Qualitatively, this means if you trace a path through this field, you will get smooth flowing curves and will not introduce extra, jagged motion. If you added this to any simulation, you'd get nice, little turbulent motion. This is convenient, because this behavior comes naturally from a fluid simulation without a source of divergence, but you can avoid a lot of unnecessary simulation by faking it with curl noise.

Turbulence fields in different software are often sums of different curl noises of different frequencies.

There are so many ways to tweak a field and the fire itself!

You can come up with interesting geometric relationships with other objects in the scene.

You can post-process the simulated data instead of working with the initial conditions prior to simulation. This is an important step, because simulation can easily be the most expensive process in both computing power and time. Post-processing saves a lot of both.

You can add ramps and/or scalar multipliers to all fields! Say you wanted to dial down the contribution of the curl noise field because it is too strong. That would be easy if you had a multiplier that you could set close to 0. The more degrees of freedom you add to your system, the more fun you can have playing with sliders and get unexpected results.

I might also add that a lot of this stuff may be unnecessary. The default settings can go very far. But I think it's cool that you can control fluids like magic.


In conclusion…

These operations can all be done in Bifrost graph extension or in Houdini! Or many other generalist 3D programs.

Volumes are not limited to fluids, and can help describe fields for particle simulations, clouds, and modeling fluffy stuff. It can do a lot of things that regular polygonal meshes cannot. It's just another tool in the toolbox.

With that ends my TED talk. There's a lot to love about different kinds of volumes, noise, etc. I do intend on writing another blog about noises, how to art direct them, and the millions of applications that they have due to the many ways you can manipulate noise.

Cheers!
Michael


Previous
Previous

Noise Fundamentals for 3D Artists

Next
Next

Grass Wind Shader