|
Features

Let There be Clouds!
Fast, Realistic Cloud-Rendering in Microsoft Flight Simulator
2004:
A Century of Flight
Performance
Microsoft
Flight Simulator 2004 maintains framerates of 15 to 60 frames
per second on consumer PCs, even in overcast scenes. We achieve
this across a range of machines with CPU speeds from 700MHz through
3.0GHz. Predictably, the machines with older CPUs and video cards
pose a particular challenge.
The
heavy amount of overdraw in the clouds presents an opportunity to
improve performance. We use the impostor technique invented by Gernaut
Schaufler (see For More Information) of dynamically rendering multiple
clouds into a texture that we then display as a billboard. We create
an octagonal ring of impostor textures around the camera, each with
a 45-degree field of view. We can render hundreds of clouds into
a single impostor. Our system compares clouds in 16-square-kilometer
blocks against the ring radius and renders only the sections beyond
the radius into impostors. Cloud blocks within the radius are rendered
as individual sprites (see Figure 4).
We
allow the user to set the ring radius. A smaller ring gives better
performance but more visual anomalies, which I'll discuss later.
A larger ring means fewer anomalies but less performance gain from
the impostors, since fewer clouds are rendered into them.
We
render the eight impostors in fixed world positions facing the center
of the ring, and re-create them when the camera or sun position
has changed past threshold values. We recalculate all eight rather
than a lazy recomputation, in case the user suddenly changes the
camera orientation. Empirical results show that the user can move
through 15 percent of the impostor ring radius horizontally or 2
percent of the ring radius vertically before recalculation becomes
necessary.
To
prevent variability in framerate when rendering to impostors, we
spread out the impostor calculation over multiple frames. For video
cards that support it, we do a hardware render-to-texture into a
32-bit texture with alpha over eight frames, one for each impostor.
For the other video cards, we use a software rasterizer to render
into the texture over dozens of frames, one 16-square-kilometer
cloud block per frame. When we update to a new set of impostors,
we crossfade between the two sets.
We
translate the impostor texture vertically up or down based on the
angle between the clouds and the camera. When the clouds are displaced
more than 10,000 feet vertically from the camera, we stop rendering
them into impostors because the view angle is too sharp. The overdraw
is much less when the clouds are far away, so performance in this
situation only suffers slightly by not rendering into impostors.
Since
video memory is frequently a tight resource on consumer PCs, we
designed the impostor system to have low video memory usage. Our
ring of eight impostors, each a 256x256 texture with 32-bit color,
adds up to a video memory cost of 8 x 256 x 256 x 4 = 2 megabytes.
When crossfading, both impostor rings are rendered, which adds another
2 megabytes during the transition.
Figure
5 shows our framerate for three scenes of varying coverage, on two
machines. We chose older machines for this experiment to show that
our results scale to machines with slower CPUs and lower video memory.
System
A is a 1.7GHz Intel Pentium with 768MB of RAM and a GeForce2 GTS
video card. System B is a 733MHz Intel Pentium III with 128MB of
RAM and a Riva TNT2 video card.
On
older systems, such as 450MHz machines with 8MB video cards, the
fill rate is so expensive that even with the impostor ring radius
at eight kilometers, rendering the cell block within the ring radius
as individual sprites produces framerates that fall below 20 frames
per second for denser cloud coverage. We created a simple LOD scheme
that uses a single sprite per cloud. Since our rendering method
scales to any number of sprites in the model, a single-billboard
cloud is merely a degenerate case of the model, and we needed no
special-case code to render or shade these models.
Shading:
Every Cloud Has a Silver Lining
We
chose not to simulate scattering of light from cloud particles,
instead using simple calculations based on artist settings that
yield a reasonable approximation. This means we do not simulate
clouds casting shadows on themselves, other clouds, or other objects
in the scene. The two factors that go into our cloud-shading system
are skylight and sunlight.
As
rays of light pass from the sky through the cloud, they are scattered
and filtered by the particles within the cloud. As a result, clouds
typically have darker bottoms. To simulate this, our artists use
a color picker in 3DS Max to specify five "color levels"
for each cloud. The color level consists of a height on the cloud
with an associated RGBA color. These levels are exported in the
cloud description file.
Separately,
for a set of times throughout the day, the artist will specify a
percentage value to be multiplied into the ambient color levels
at each particular time of day. This allows the ambient contribution
to decrease approaching night.
The
sun casts directional light on a cloud, which generates dramatic
scenes, especially around dawn and dusk. We simulate the effect
so that areas of the cloud facing the sun receive more directional
light while areas facing away from the cloud receive less.
Our
artists specify shading groups, sections of one of 30 sprites that
are shaded as a unit, when they build the clouds in 3DS Max from
boxes. On each box, they set a custom user property with a shading
group number, and sprites generated for that box will belong to
that shading group. These shading groups simulate clumps on the
cloud. We calculate the directional component of shading for a given
vertex in the cloud by first computing the vector to that point
from the shading group center. We also find the vector from the
group center to the sun and compute the dot product of the two vectors
after normalization.
The
artist specifies a maximum directional color and minimum and maximum
directional percentages for a range of times throughout the day.
We multiply the resulting percentage from the mapping function with
the maximum directional color specified by the artist, to get the
directional color of the vertex.
To
get the final vertex color, we add the ambient and directional colors
to the color from the sprite texture. At this point, we also multiply
by the alpha value representing formation or dissipation of the
cloud.
______________________________________________________
|