Simulating
gases using a procedural material
by François Guillet
This tutorial owes a
lot to "Texturing & Modeling A
Procedural Approach" by Elbert, Musgrave, Peachey, Perlin,
Worley Morgan & Kaufmann
In
this tutorial, I will illustrate how gases or gas-like environment can
be simulated using a procedural material. We will address two effects
such a volume has on light:
-absorption
-scattering
Absorption
is directly related to the density parameter of a material. When
the density increases, so does the material light absorption : it
becomes more
and more visible. Scattering means that each point of the material
scatters
incoming light, thus becoming a tiny point light source itself. This
probably
explains why this feature costs a lot in terms of rendering time. Some
systems,
like dust in a house, do not show up until directly hit by sun rays.
This
probably means that absorption is not the main characteristic of this
phenomenon, but rather scattering. On the other hand, some other
phenomenons,
like water vapor rising from a cup of hot water, cause light absorption.
Anyway,
when simulating gases, both parameters often vary in space: the
effect is not uniform. As soon as a property is not uniform over a
whole
volume, a procedural material has to be used. In the following
examples, we
will try to simulate vapor raising from a cup of hot liquid in
different ways.
The
first step is to create a sample scene with a cup and a dummy object to
host our effect through its material:

In
this case, the dummy object will be a cylinder in which the vapor
will “rise”. In order to control of x,y,z coordinate ranges, it is best
to
convert the cylinder to a triangle mesh and have the vertices
coordinates start
at y=0 as shown below. This will be helpful when we’ll use the
coordinates to
determine density and scattering.

We
now have to calculate vapor density inside this volume. Quite
obviously vapor density is maximum just above the liquid surface and
near the
center of the cup. Roughly, steam occupies half a sphere above the cup.
It is
maximum at the center of the sphere and radially decreases. It also has
a
“nebulous” aspect which is quite well approximated by the “Turbulence”
procedural motif. Turnulent module default parameters will be used,
except an amplitude of 1.7 instead of 1 which will make the turbulent
gas 'thicker'. We also have to determine a density for each x,y,z point
and
multiply this density with the turbulence motif. The density has to be
one at
x=0, y=0, z=0 (the bottom of the cylinder) and falloff radially. This
is done
using the following procedural material which has to be assigned to the
cylinder. Also, do not forget to use a totally transparent texture for
the
cylinder as well.


The
principle of the procedure is to calculate the distance from the
x,y,z point to the origin. The double of
this distance is subtracted from one in order to decrease density which
must be
zero or negative when the cylinder boundary is met (a density or
scattering
below one will be interpreted as null). The result is multiplied by a
turbulence motif, and plugged in Density and Scattering parameters
(depending on what you really want to do, different results could be
used for one parameter or the other).
You may use any one of these two or a combination as you see fit. The
render
shows a tiny hemisphere of vapor above the cup. Whereas this result is
OK in
the x,z plane, the vapor is expected to rise higher along y direction.
A
solution is to confine the vapor not within a sphere but whithin a
taller
ellipse. This is simply done through altering the procedure as shown
below. The
0.3 scaling factor on Y ensures that the ellipse is 1/0.3 taller than
the
sphere.


That’s
more like it! But in fact, we would expect the steam to “expand”
above the cupas disperses. A solution is to use a cone instead of a
cylinder
and have the vapor fill this cone. However, we can’t really modify our
mathematical ellipse to fill the cone. We will switch to a formula that
suits
the conical shape. The first step is to transform the cylindrical
triangle mesh
into a cone. Next, the material procedure is modified as shown below.


Only
the radius in the x,z plane is computed. This radius is multiplied
by a negative quantity which increases (absolute value decreasing) as y
increases. The 0.7 coefficient makes it increase slower because
preliminary
tests showed that conical expansion was too fast. As a result, density
decreases less when computed far from the cone center when y increases.
And
here is the render:

Obviously,
there is far too much steam at the top of the cone. It should
be less dense as it disperses. So we will introduce another correction
which
will diminish density as a function of y. After some trials, I came up
whith a
1/(y+1) decreasing function. I scaled it to 1/(1/3*(y+3)) after final
tuning. The
point is that the function must decrease as y increase. It must
compensate a
volume increase by y due to the cone (y cone slices volume increase
linearly
with y). So steam volume increase as y and it must be multiplied by 1/y
to be
kept constant. Since the function must yield a density of 1 at y=0, the
function to use is something like 1/(y+1). Here is the actual procedure
used,
and the resulting render:


The
result is fine, but it can be further refined (or complexified) with
the consideration that the steam does not rise in a turbulent way when
it is
just coming up from the cup. It is first homogeneous and then becomes
turbulent. This effect can be simulated through adding a fraction of
constant
density steam to the complementary fraction of turbulent steam. Say
that at
some point y we decide to have a p fraction of turbulent density steam,
then:
Total
stream = ( 1 - p )* constant density steam + p * turbulent density
steam
Of
course, p is a function of y and reaches 1 at some point. After that
point, it remains constant at this value. We will use a cutomized
function to
define p=f(y). The final procedure is thus:

It
is admittedly complex. First we use
the 0.7*y value that has already been used before to fill the
cone with
steam. This value is input in a customized function that is reproduced
below.
Please note that this function output doesn’t start at 0 but at 0.6.
Values
below that generate far too much steam, and in fact a majority of
turbulent
steam is already needed at y=0. It reaches 1 (fully turbulent steam)
when 0.7*y
= 1. the resulting value p is multiplied with the turbulent motif and
1-p is
added to the result.

The
result now looks like this:

Of
course, I do not claim that this procedural material is the best one
for simulating steam over a cup of coffee, but this shows you what can
be done
using procedural materials.
File for spherical steam
can be downloaded here:
Steam_sphere.aoi
File for conical steam
can be downloaded here:
Steam.aoi