Guideline for making 3D models for Ultimate Stunts
Introduction
Ultimate Stunts is a 3D game, and it uses 3D models to build the track from and
for the cars. These 3D models can be created in external 3D modelling software,
imported with the stunts3dedit program, transformed and colored, and saved in
Ultimate Stunts' internal .glb format. This document does not describe how to do
this. Instead, it describes what kind of 3D models fit the goals of Ultimate
Stunts.
Goals for good 3D models
A 3D model is good if it meets two objectives: it has to look good, and it has
to be displayed fast. Of course, these objectives often have opposite effects on
the 3D model: a good looking model is displayed slowly, and a fast displayed
model often looks bad. In this document, some tricks will be given to make 3D
models both fast and looking good.
Performance requirements, and target platforms
Of course, the right equilibrium between performance and detail depends on the
hardware. Ultimate Stunts uses openGL, which can be many times faster when it
uses hardware acceleration. The Ultimate Stunts project tries to be very
flexible in its hardware requirements: people with poor hardware should be able
to get an acceptable frame rate (with poor graphics) , and people with good
hardware should get high-quality graphics. The user can set various graphics
settings in Ultimate Stunts, like the texture level of detail,
enabling/disabling blending of translucent surfaces and even enabling/disabling
the z-buffer.
Level Of Detail (LOD)
For the 3D geometry designer, it is important to know that
Ultimate Stunts uses different Levels Of Detail (LOD) in different situations.
The 3D geometry of every LOD should be defined in the .glb file (of course you
can use a single model in each LOD, but that's not the optimal situation).
Currently there are six LODs: 1 (most detail) to 4 (least detail),
c (for collision detection) en s (for drivable surfaces).
Polygon count
Even on high-end hardware, the number of polygons in a model can make it too
slow. Every polygon has a number of vertices, and for every vertex a number of
transformations and tests has to be applied in order to project it onto the
screen's surface. Actually, the vertex count is more important than the polygon
count; there can be a difference if vertices are shared between polygons.
Of course a model with fewer polygons is always less detailed than a model with
more polygons. Especially curved surfaces can look bad if they are made of too
few polygons. A number of tricks can be applied to hide the fact that a model
has only a few polygons:
- Curved surfaces can be made with smooth shading, or
"Gouraud shading". Normally the eye can see the boundary between the
faces because the faces have different colors intensities, which is
caused by the different orientations of the faces relative to the light
source. With "flat shading", every polygon has a uniform color, so at
the boundary between polygons of a curved surface, the user can see an
intensity difference. With "smooth shading", the shadow intensity is
not calculated per face, but per vertex. Within the polygon, the
intensity is interpolated between the vertices. Now, as two polygons of
a curved surface that are next to each other share vertices, their
color intensities at the boundary are also the same. The result is that
the user cannot see the boundary between the polygons.
In order to let this work, a "normal vector" needs to be defined for
every vertex. The normal vector defines the direction of the surface at
the position of the vertex, which is used to calculate the shadow
intensity. Many 3D modelling programs can generate these normal vectors
automatically, by enabling "smooth shading" for a group of polygons.
The effect of smooth shading can really be spectacular. A cylinder can
be made with only 10 polygons, while the low number of polygons is only
visible at the flat sides of the cylinder. Generally, it's a good idea
to use more polygons for large and important objects, like loopings and
wheels, and less polygons for small and unimportant objects, like in
scenery. You could also make it different for different LODs.
-
Many details can be done with textures instead of polygons. For
instance, a door of a building can be a single polygon with a door
texture on it. A fence can be built of many cylinders, each having many
polygons, but it can also be made of a single polygon with a fence
texture on top of it. Textures are mapped on polygons by defining
"texture coordinates" for every vertex. For every pixel of the polygon,
the texture coordinates are interpolated, and the color corresponding
to those coordinates is found in the texture image. These operations
do not really depend on the texture size, so they can be done much
faster than 3D vertex transformations for an equally detailed model made
of polygons instead of textures. So, if you want both speed and quality:
use textures.
Textures
So, textures can be used to make highly detailed models with a low polygon
count. The disadvantage is the extra number of per-pixel calculations required
to find the texture color of that pixel. On hardware-accelerated systems, those
operations are done by the video card processor instead of the main processor.
The video card processor is optimised for doing the same thing many times
simultaneously, so it can do the per-pixel operations many times faster than the
main processor. The problem is that the video processor needs access to the
texture data. With modern AGP/PCIe ports the communication between video card and
CPU/main memory is really fast, but transferring all texture images at a 25 fps
rate would still to much bandwidth. That's why textures should be placed in the
video card's memory, and that's why it's important to have enough video card
memory.
For the 3D modeller, it's important to realise that the performance will drop
down if not all textures fit in the video memory. Of course the memory usage
can be reduced by using smaller textures (even the user can do this in
Ultimate Stunts' settings: Ultimate Stunts resizes textures before it
gives them to openGL). This resizing is so simple that it's not really
important for the modeller. Another way to reduce texture memory is
important to know: sharing textures between models. A texture that is used by
many models is loaded only once, so it uses memory only once. So instead of
using a different grass texture on every tile, it's better to use the same
grass texture.
Textures for Ultimate Stunts can also have an alpha layer, which can be used for
transparency effects. For example, the leaves of a tree can be drawn in a
texture with an alpha layer, with the part of the texture outside the leaves
being transparent. Then a tree can be made with very few polygons, while they
are still very detailed. A disadvantage of textures with very large transparent
areas is that for many pixels, a lot of calculations are made to get to the
conclusion that nothing has to be drawn. For large objects (that appear large
on the screen, so that they take a lot of pixels) it could be better to define
a more detailed polygon model and to reduce the number of unneccesary pixel
operations. Another disadvantage is that for users who disable transparency
blending, the transparent areas display as opaque. For these users, a more
detailed polygon model might be a better solution.
The z-buffer
In a correctly rendered 3D image, objects that are in front of other objects
cover those other objects, so that they are not visible. There are a couple of
ways to reach this goal. One way is to sort the objects and draw them from back
to front. When an object is drawn on top of another one, it replaces those
pixels, and the result is correct. Another way to do it is to remember the
depth of each drawn pixel (the z-coordinate) in a buffer, the z-buffer. When a
new pixel is drawn, its depth is compared to the existing pixel, and the new
pixel is only drawn if it is in front of the old one. This method is more
accurate than the other one, because it compares per-pixel instead of
per-object. In theory it should always give the correct result, but in reality
there are some problems.
One of these problems is that the z-buffer has a limited accuracy. Polygons that
are very close together, like a window on top of a wall, sometimes get mixed
together in an ugly way because the z-buffer cannot see which of them is closer
to the camera.
Another problem has to do with translucent surfaces. When those surfaces
are drawn before the objects behind the surface are drawn, then the objects
behind the surface are not drawn because the z-buffer values are set to the
depth of the translucent surface. Ultimate Stunts solves this by trying to
render objects from back to front as much as possible, but inside an object it
doesn't change the drawing order. This drawing order should be set in such a
way that it gives a correct result from most viewpoints. The drawing order is
also important if no translucent surfaces are in the object, because the
z-buffer can be turned off for performance reasons. The only thing that keeps
things right in such situations is the drawing order.
Design decisions
So, concluding, the following decisions have to be made:
- The number of faces for round shapes. You can vary this for
different LODs. Generally, a low number is good enough if you use
smooth shading.
- The details that are in textures versus details that are in
geometry. Generally, texture is better if the player doesn't need to
see depth differences.
- Recycling of textures: use textures that are already used in
Ultimate Stunts, or use textures that can be re-used in the future.
Collision detection
I forgot to tell you about collision detection. Till now, we've only been
talking about the graphics, but the geometry of objects also influences their
physical interaction by collisions. There are separate LODs ("c" and "s") for
the collision model.
The collision models for tiles, car bodies and car wheels are defined
differently. Tile collision models are a set of polygons, just like the graphics
model, so for many tiles the graphics model can also be used for collision
detection.
The only special thing about tiles is that "surface" polygons should be in the "s"
LOD, and not in the normal "c" LOD. This is because Ultimate Stunts treats
drivable surfaces differently from collision surfaces. Water surfaces should be
part of both "s" and "c".
The car's body collision model is a "bounding volume", which
approximates the body's shape with a set of planes. Every plane splits the
entire space in two parts, and says "the car is on this side of the plane".
With enough planes (with cleverly chosen orientations), only a small volume
remains, and this volume should be an approximation to the shape of the car.
The planes are defined with polygons in the "c" LOD. In the existing cars you
can see examples of collision models, where the collision planes are usually
translucent blue.
The wheels have cylindrical collision models. The cylinder is defined by two
polygons at the positions of the "end caps" of the cylinder. (the wheel's
symmetry axis is the x-axis, so those polygons should be perpendicular to the
x-axis.) The height (y-direction) of those polygons defines the diameter of the
cylinder. Again, you can see examples in existing cars.
A very detailed collision model can make the collision detection very
time-consuming, and luckily a detailed collision model is not needed as much as
a detailed graphics model. Some complex shapes with many polygons can be
approximated with a simpeler collision model, and some objects don't need a
collision model because the cars wil never collide with them. In some situations
it is even desirable that an object doesn't generate collision effects, e.g.
with bushes or flowers.