DirectX® 9.0 and SmartShader™ 2.0 White Paper
Car Paint

This demonstration exemplifies some of the more innovative uses
for DirectX 9.0 pixel shaders. In particular, it uses an interesting
two-tone car paint effect, as well as a new normal mapping technique
that allows 3D objects with low polygon counts to appear to
have much more detail.
The car's paint is made up of three layers. The base layer consists
of a range of colors, which are stored in a one-dimensional
texture. The color used for a given pixel is sampled from this
texture according to the viewing angle. In the default example,
parts of the car viewed head-on appear yellow, while those viewed
at sharp angles or nearly edge-on appear red. Pixels viewed
at intermediate angles appear to have a blend of these two basic
tones.
The second layer consists of tiny metal flakes or "sparkles"
that give some car paints a metallic look. Since these flakes
are flat and two-dimensional, they only appear to reflect light
when viewed head-on, and seem to disappear when viewed edge-on.
To recreate this effect, a subtle noise texture is used. This
texture is blended with the base layer to create small variations
in the brightness of each pixel, and is also used to perturb
the base texture to make the transitions from yellow to red
appear fuzzier and less distinct. The result is that the parts
of the car viewed directly appear more "sparkly" than those
viewed at a sharp angle, which convincingly reproduces the light
scattering behavior of real-life car paint.
The third layer is a glossy, transparent clear-coat layer. This
layer is simulated using a cube environment map, which is projected
on to the surface of the car as a reflection. With the advanced
pixel shader capabilities of the Radeon 9700 & 9500 series GPUs,
all three layers of the car paint can be generated in a single
rendering pass, meaning this complex effect can be handled effortlessly
and applied to multiple objects in a scene.
This demo makes extensive use of normal maps (also known as
bump maps) on the car and the turntable. A normal is a three-dimensional
value that determines the facing of a surface at any given point,
which in turn determines the direction that light will be reflected.
Since a standard polygon is a flat surface, all of its normals
by default point in the same direction. By varying the normals
across a flat surface, a normal map can create the illusion
of fine details and smooth curvature without requiring additional
polygons.
|
|
|
|
The tricky part about using this technique is the generation
of high quality normal maps. There are two primary difficulties:
authoring the detailed normal map, and having the necessary
precision to faithfully reproduce subtle details. An innovative
way to address the first issue is to start with a very high
polygon count model, and then create a second version of the
same model with a much lower polygon count. For example, the
original car model used in the demo was comprised of over one
million polygons, while the version actually displayed has only
a few thousand polygons. Most 3D game artists are already accustomed
to this process, since game engines tend to severely restrict
the polygon counts that can be used in order to maintain reasonable
performance across a wide range of system configurations.
Once the two versions of the model are completed, they are run
through a ray-casting algorithm that calculates the differences
between them. These difference values are then filtered and
stored as a high precision normal map, which can be applied
to the low polygon model while maintaining all of the fine surface
detail from the high polygon model. The car paint demo uses
normal maps with 16 bits per component. DirectX 8.1 and earlier
hardware are limited to just 8 bits per component for normal
maps, which can result in image artifacts on subtly curved surfaces.
High precision normal maps can get quite large, and they cannot
be easily compressed using standard texture compression methods
like DXTC. The car paint demo, however, makes use of a new square
root operation introduced in the 2.0 Pixel Shader specification
to implement a simple yet effective normal map compression scheme.
Each location in a normal map requires three pieces of data,
representing the x, y & z components of a 3D vector. A pixel
shader using the Pythagorean theorem ( ) to derive the z components
from the x & y components makes it possible to compress the
normal map to just 2/3 of its original size, with no loss of
data.
|