Three-dimensional rendering introduces a
variety of new challenges: 3D graphics are rendered as models rather
than as simple bitmaps, our brains need to shift into a different gear
to keep track of movement in and out of the screen, and we have an
entirely different way of telling XNA where we want to draw things on
the screen.
Let's start by discussing some of the features of the 3D rendering environment.
1. Matrix-Based Positioning
As we prepare to render 3D graphics, we leave this
approach behind for the time being. Instead, we use a system based around matrices.
Matrices allow us to encode a series of movements
and transformations into a compact structure that can then be applied
to the graphics that we want to draw. Most of the calculations required
to do this are conveniently wrapped up in handy XNA functions, so we
don't need to get too involved in their inner workings.
Just like anything else, it might take a little time
to become accustomed to thinking with matrix transformations, but once
you do you will find them a very useful tool. In fact, you might
ultimately decide that you prefer them to XNA's sprite rendering
approach.
2. Abstract Coordinate System
When we render in 3D, XNAgenerally uses an abstract
coordinate system rather than a pixel-based coordinate system like the
one used for sprite rendering, meaning that we are not concerned with
pixels. Although this might sound like a disadvantage at first, freeing
ourselves from pixel coordinates actually turns out to be rather useful.
When we initialize XNA, we can tell it the
dimensions of the screen and the coordinate system will scale to match.
Moving a graphic object a certain distance to the right, therefore,
moves the same distance regardless of the back buffer size. As a
result, should we decide to use a smaller buffer to increase
performance, none of the rendering code needs to change (as it did when
we were rendering sprites).
After all the time we have spent with sprites,
getting to grips with the 3D coordinate system requires a slight twist
of the brain. First of all, the coordinate (0, 0) is generally right in
the center of the screen rather than in the top-left corner. Second,
movement along the positive y axis will travel up the screen, as
opposed to down for sprites. It can be a nuisance having to keep these
two conflicting coordinate systems in your brain, but once you are in
the frame of mind for one system over the other, it should be easy to
remember which way is up.
Becausewe are now using a 3D graphical environment,
we actually need to add a third element to our coordinates. The
coordinate values we have looked at in the past have been in the form
of (x, y), representing the specified distances along the x and y axes.
3D coordinates are in the form (x, y, z), providing values in the z
axis as well as the x and y axes. The z axis represents movement into
or out of the screen—literally the third dimension. Positive values on
the z axis result in movement toward the player, negative values result
in movement into the screen.
3. Drawing Primitives
When it comes to drawing graphics, XNA is actually
not able to draw anything more complex than triangles. This might at
first seem very restrictive, but in fact, it is not as you will see
when we start to use it in some example projects.
The reason we can create more complex scenes is
partly because much more complex shapes can be created by putting lots
of triangles together (for example, a rectangle is just two triangles
joined along their long edge) and partly because we can put graphic
images onto the triangles.
When we are drawing, we refer to each triangle as a surface. The points that form the triangle are called vertices. Figure 1 shows two triangular surfaces created using four vertices. Two of the vertices are shared between the triangles.
The vertices themselves are not actually displayed by XNA, just the surfaces that they define; the vertices are shown in Figure 1 just to clarify what they are.
The only primitives available other than triangles are lines.
4. Textures
Just as we used Texture2D objects to
provide graphics for our sprites, so we can use them to fill the
triangles that we are rendering. We have a lot of flexibility to use
textures within our applications—much more so than we had with sprites.
We can take small rectangular sections just as we did with sprites, or
we can stretch textures in a variety of different ways across the
shapes that we draw.
5. XNA is a State Engine
Whereas, with sprite rendering, each individual call
to draw graphics provided all the information needed for drawing to
take place, the approach for 3D rendering is slightly different. XNA
maintains lots of state values for
things such as which texture is currently being used for rendering,
whether transparency is enabled, whether lighting is switched on, and
so on.
In order for our rendering to appear as we expect,
each of these states must be set prior to the rendering call. Once a
state has been set, it will stay with its value until we decide to
change it again.
NOTE
All this actually applies to sprite
rendering, too, except that the sprite engine always sets the state
values according to the parameters passed to the SpriteBatch.DrawDrawString methods.
and