Beginning DirectX9 Chapter 4 - 3D Primer
Wendy Jones
Èñòî÷íèê: Beginning DirectX9 Wendy Jones 2004 by Premier Press
You’ve probably noticed that 2D games have been on the decline for the past few
years. Recently, games have been all about pushing the power of the latest 3D
video cards, trying to bring more reality to the games being played. Direct3D is a
key component in the 3D wave. It allows millions of Microsoft Windows users to experience
the latest technologies in games.
Here’s what you’ll learn in this chapter:
- How 3D space is used
- What coordinate systems are
- How to plot the points of a polygon
- How Direct3D defines vertices
- What a vertex buffer is
- How to define a 3D scene
- The different primitive types available to you
3D Space
Previously, I talked about games that only allowed movement in two directions, which
created a rather flat world. The sprites you created before resided in a world with width
and height but no depth; the sprites existed in a two-dimensional world.
Direct3D gives you the power to take your game world one dimension further with the
addition of depth. Depth is the ability for objects to move farther away or closer to the
viewer. Characters within a three-dimensional world are more realistic than their 2D
counterparts.
3D space is the area in which your three-dimensional world exists. 3D space enables your
characters to move around in a way that is similar to the real world. Before you can take
advantage of 3D space, you need to know how that space is constructed and how objects
are placed into it.
Coordinate Systems
Coordinate systems are a way of defining points within a space. They consist of a set of
imaginary grid-like lines—called axes—that run perpendicular to one another. A 2D
coordinate system contains only two axes, whereas a 3D system adds one more. The center
of a coordinate system, where the axes intersect, is called the origin. Figure 4.1 shows what
a standard 2D coordinate system looks like. The two axes in a 2D coordinate system are
referred to by the letters X and Y. The X axis is horizontal, whereas the Y axis is vertical.
You’ll notice in Figure 4.1 that the center X and Y lines are darker than the rest. These lines
define the center for each axis and have a value of 0.
Defining a Point in 2D Space
A point is defined as a single position along an axis. A point in 1D space, which consists
of a single line, would be referred to by a single value. Figure 4.2 shows a single point plotted
on a line. The origin of the line is represented by the value of 0. Points to the right of
the origin have a positive value, whereas those to the left of the origin have a negative
value. In Figure 4.2, the point has a value of positive 4.
Figure 4.1 How a 2D coordinate system is laid out. Figure 4.2 A 1D coordinate system.
2D coordinate systems, because they have two axes, require a second value to plot a point.
To plot a point within 2D space, you need to define a position along both the X and Y axes.
For instance, a single point in a 2D coordinate system would be referred to by two
numbers—an X value and a Y value—each number referring to the point’s position on that
axis.
Like the 1D example shown in Figure 4.2, the values on the X axis continue to increase to
the right of the origin, but the values on the Y axis increase as you go up from the origin.
Figure 4.3 shows a 2D coordinate system with a point plotted at an X value of 3 and a
Y value of 5. You’ll commonly see points shown as (X, Y). In this instance, the point would
be shown as (3, 5).
Defining a Point in 3D Space
As I mentioned earlier, a 3D coordinate system adds an extra axis, called the Z axis. The Z
axis is perpendicular to the plane created by the X and Y axes. Figure 4.4 shows how a 3D
coordinate system would look.
Notice that the coordinate system has the Z axis pointing off into the distance. Commonly,
the X and Y axes describe width and height, and the Z axis describes depth.
The Z axis can have either a positive or negative value as it moves away from the origin
based on the layout of the coordinate system. Coordinate systems are commonly laid out
in either a left-handed or right-handed manner.
Figure 4.3 A point plotted on a 2D coordinate Figure 4.4 A 3D coordinate system layout.
Left-Handed Systems
A left-handed coordinate system extends the positive X axis to the right and the positive
Y axis upward. The major difference is the Z axis. The Z axis in a left-handed system is
positive in the direction away from the viewer, with the negative portion extending toward
him. Figure 4.5 shows how a left-handed coordinate system is set up. This is the coordinate
system used in Direct3D.
Right-Handed Systems
The right-handed coordinate system, which is the system used by OpenGL, extends the X
and Y axes in the same direction as the left-handed system, but it reverses the Z axis. The
positive Z values extend toward the viewer, whereas the negative values continue away.
Figure 4.6 shows a right-handed system.
Vertices Explained
A vertex is similar to the idea of the point that I described earlier. A vertex includes information
such as location along the X, Y, and Z axes, but it can include other information
as well, such as color or texture.
Figure 4.5 A left-handed coordinate system. Figure 4.6 A right-handed coordinate system.
When describing a vertex in code, you can use a structure similar to this:
struct {
float x;
float y;
float z;
} vertex;
This vertex structure contains three variables—
each one of type float—that describe the vertex’s
location along each axis.
Creating a Shape
You can create shapes by using two or more vertices.
For instance, in the creation of a triangle,
three vertices would be needed to define the three
points of the triangle. Plotting a shape with vertices
is similar to playing connect the dots. Figure
4.7 shows how to create a triangle by using three
vertices.
Figure 4.7 Three vertices create a triangle.
Defining this triangle in code requires three vertex structures.
struct {
float x; // the X coordinate
float y; // the Y coordinate
float z; // the Z coordinate
} vertex [ 3 ];
Here, I’ve changed the vertex structure to define an array of three vertices. The next step
is setting each of the vertices to the positions found in Figure 4.7.
// Set the first vertex
vertex[0].x = 2.0;
vertex[0].y = 4.0;
vertex[0].z = 0.0;
// Set the X coordinate
// Set the Y coordinate
// Set the Z coordinate
// Set the second vertex
vertex[1].x = 5.0;
vertex[1].y = 1.0;
vertex[1].z = 0.0;
// Set the X coordinate
// Set the Y coordinate
// Set the Z coordinate
// Set the final vertex
vertex[0].x = 2.0;
vertex[0].y = 1.0;
vertex[0].z = 0.0;
// Set the X coordinate
// Set the Y coordinate
// Set the Z coordinate
Notice that the Z coordinate of all three vertices was set to 0.0. This triangle has no depth,
so the Z coordinate remains at 0.
n o t e
Triangles are the simplest closed shapes that you can plot with vertices. You can create more complex
shapes, such as squares or spheres, but they are broken down into triangles before rendering.
Adding Color
Previously, the vertex structure only included information that related to the position of
each vertex. However, vertices can also contain color information. This color information
can be held in four additional variables called R, G, B and A.
- R. The red component of the color
- B. The green color component
- B. The blue color component
- A. The alpha color component
Each of these values helps to define the final color of the vertex. You can see the updated
vertex structure next.
struct {
// position information
float x;
float y;
float z;
// color information
float R;
float G;
float B;
float A;
} vertex;
Using the R, G, B, and A variables, you can set the color of the vertex. For instance, if you
want your vertex to be white in color, the R, G, and B variables should be set to 1.0.
Setting the vertex to a pure blue color requires R and G to be set to 0.0, whereas the B
variable is set to 1.0.
n o t e
The alpha component of a color determines its transparency level. If the value of an alpha component
is 0, the color specified in the R, G, and B values is completely opaque. If the alpha value is
greater than 0, the color specified has a level of transparency. The alpha component can be any
value between 0.0f and 1.0f.
Vertex Buffers
Vertex buffers are areas of memory that hold the vertex information needed to create 3D
objects. The vertices contained within the buffer can contain different kinds of information,
such as position information, texture coordinates, and vertex colors. Vertex buffers
are useful for storing static geometry that needs to be rendered repeatedly. Vertex buffers
can exist in either system memory or the memory on the graphics adapter.
A vertex buffer is created by first declaring a variable of type IDirect3DVertexBuffer9. The
resulting pointer refers to the vertex buffer that DirectX will create for you.
Next, your game must create the vertex buffer and store it in the variable you created.
After you have a valid vertex buffer available, you need to fill it with vertices. You can do
this by locking the vertex buffer and copying the vertices.
Creating a Vertex Buffer
You can create vertex buffers by using a call to CreateVertexBuffer. The CreateVertexBuffer
function, defined next, requires six parameters.
HRESULT CreateVertexBuffer(
UINT Length, DWORD Usage,
DWORD FVF, 3DPOOL Pool,
IDirect3DVertexBuffer9** ppVertexBuffer,
HANDLE* pHandle
);
- Length. Variable containing the length of the vertex buffer in bytes.
- Usage. Flags that determine the behavior of the vertex buffer. This value should
commonly be 0.
- FVF. The flexible vertex format that the vertex buffer uses.
- Pool. The memory pool where the vertex buffer resides. This value is an enumerated
value of type D3DPOOL.
- ppVertexBuffer. An address to a pointer of a variable of type IDirect3DVertexBuffer9.
This variable holds the newly created buffer.
- pHandle. A reserved value, this should always be NULL.
The vertices within a vertex buffer can contain flexible vertex information. Basically, this
means that the vertices in the buffer can include just position information, or they can
include colors or texture coordinates. The type of data contained within the vertices of the
buffer is controlled by the Flexible Vertex Format (FVF) flags.
Flexible Vertex Format
The Flexible Vertex Format allows for customization of the information stored in the vertex
buffer. By using a set of FVF flags, the buffer can be made to contain any number of vertex
properties. Table 4.1 describes the FVF flags in move detail.
Table 4.1 Flexible Vertex Format Flags
Flag |
Flag Description |
D3DFVF_XYZ |
Vertex format includes the X, Y, and Z coordinate of an untransformed vertex. |
D3DFVF_XYZRHW |
Vertex format includes the X, Y, and Z coordinates, but this time they are already transformed. |
D3DFVF_XYZW |
Vertex format contains transformed and clipped vertex data. |
D3DFVF_NORMAL |
Vertex format contains normal information. |
D3DFVF_PSIZE |
Format includes the point size of the vertex. |
D3DFVF_DIFFUSE |
Diffuse color is part of the vertex buffer. |
D3DFVF_SPECULAR |
Specular information is part of the vertex buffer. |
|
|
D3DFVF_TEX0 |
Texture coordinate 0. |
D3DFVF_TEX1 |
Texture coordinate 1. |
D3DFVF_TEX2 |
Texture coordinate 2. |
D3DFVF_TEX3 |
Texture coordinate 3. |
D3DFVF_TEX4 |
Texture coordinate 4. |
D3DFVF_TEX5 |
Texture coordinate 5. |
D3DFVF_TEX6 |
Texture coordinate 6. |
D3DFVF_TEX7 |
Texture coordinate 7. |
D3DFVF_TEX8 |
Texture coordinate 8. |
Direct3D can handle up to eight sets of texture coordinates per vertex.
The format of the vertices you use is created by defining a custom vertex structure. The
following structure defines a vertex that contains untransformed position information, as
well as a color component.
struct CUSTOMVERTEX
{
FLOAT x, y, z, rhw; // the untransformed, 3D position for the vertex
DWORD color; // the vertex color
};
The CUSTOMVERTEX structure consists of the standard vertex position variables X, Y, and Z,
but also includes the variable RHW. The RHW value, which stands for Reciprocal of Homogeneous
W, tells Direct3D that the vertices that are being used are already in screen coordinates.
This value is normally used in fog and clipping calculations and should be set to 1.0.
n o t e
The vertex color is a DWORD value. Direct3D provides a few macros to assist you in creating these
colors. One such macro is the D3DCOLOR_ARGB(a, r, g, b) macro. This macro accepts four components:
an alpha, a red, a green, and a blue. Each of these components is a value between 0 and
255. This macro returns a DWORD color value that Direct3D can use.
D3DCOLOR_ARGB(0, 255, 0, 0) creates a color value representing all red.
Additional macros are D3DCOLOR_RGBA and D3DCOLOR_XRGB, which are described fully in the DirectX
documentation.
Now that the vertex structure is created, the next step is determining the flags that will be
sent to the CreateVertexBuffer function as the FVF parameter.
Because the CUSTOMVERTEX structure requires non-transformed position information and a
color component, the needed flags would be D3DFVF_XYZRHW and D3DFVF_DIFFUSE.
The sample code that follows shows a call to CreateVertexBuffer using this vertex structure.
// a structure for your custom vertex type
struct CUSTOMVERTEX
{
FLOAT x, y, z, rhw; // the untransformed, 3D position for the vertex
DWORD color; // the vertex color
};
// Create the variable to hold the vertex buffer
LPDIRECT3DVERTEXBUFFER9 buffer = NULL;
// variable used to hold the return code
HRESULT hr;
// Create the vertex buffer
hr = pd3dDevice->CreateVertexBuffer(
3*sizeof( CUSTOMVERTEX ),
0,
D3DFVF_XYZRHW | D3DFVF_DIFFUSE,
D3DPOOL_DEFAULT,
&buffer,
NULL );
// Check the return code
if FAILED ( hr)
return false;
As you can see, the CUSTOMVERTEX structure is created first, telling Direct3D the type of vertices
to use. Next, the call to CreateVertexBuffer creates the actual buffer and stores it in the
buffer variable.
The first parameter to CreateVertexBuffer, the size of the buffer in bytes, is created with
enough space to hold three vertices of type CUSTOMVERTEX.
The third parameter, the FVF, is shown as having the flags D3DFVF_XYZRHW and D3DFVF_DIFFUSE
being used.
The fourth parameter sets the memory pool for this vertex buffer. The value
D3DPOOL_DEFAULT is used, which allows the buffer to be created in the most appropriate
memory for this type.
The final parameter that you have to worry about is the fifth one. This is where you pass
in the variable that holds the newly created buffer.
After the call to CreateVertexBuffer is complete, make sure to check the return code to confirm
that the buffer was created successfully.
Loading Data into a Buffer
Now that you have a valid vertex buffer, you need to add vertices to it. Before you can place
vertices in the buffer, you must lock the memory the buffer is using. After this memory is
locked, it is freely available to be written to by your game.
Locking the Vertex Buffer
Locking the memory used by the vertex buffer allows your application to write to it. At
this point, you’ve already defined the vertex buffer and the type of vertices it will hold. The
next step is locking the buffer and copying the vertices. Locking of the buffer is accomplished
with the Lock function, defined next.
HRESULT Lock(
UINT OffsetToLock,
UINT SizeToLock,
VOID **ppbData,
DWORD Flags
);
The Lock function takes four parameters:
- OffsetToLock. The offset into the buffer to lock. If you want to lock the entire buffer,
this value should be 0.
- SizeToLock. The size in bytes to lock. Again, if you are locking the whole buffer, this
value should be 0.
- ppbData. A void pointer to the buffer that holds the vertices.
- Flags. Flags that describe the type of lock. Following are the available flags:
- D3DLOCK_DISCARD. The entire buffer is overwritten.
- D3DLOCK_NO_DIRTY_UPDATE. Dirty regions of the buffer are not written to.
- D3DLOCK_NO_SYSLOCK. The system keeps normal display mode changes from
happening during a lock. This flag enables the system to continue processing
other events.
- D3DLOCK_READONLY. The buffer cannot be written to.
- D3DLOCK_NOOVERWRITE. Any information currently in the buffer is not to be
overwritten.
The following code sample shows a normal call to the Lock function.
HRESULT hr;
VOID* pVertices;
// Lock the vertex buffer
hr = g_pVB->Lock( 0, 0, ( void** ) &pVertices, 0 );
// Check the return code to make sure the lock was successful
if FAILED (hr)
return false;
The Lock function assumes that you’ve already created a valid vertex buffer. The variable
g_pVB refers to this buffer.
Copying Vertices to a Vertex Buffer
After the vertex buffer is locked, you can freely copy data into the buffer. You can either
copy enough vertices to fill the whole buffer, or you can selectively change vertices within
the buffer. The next example shows how to use memcpy to copy an array of vertices into a
vertex buffer.
// the customvertex structure
struct CUSTOMVERTEX
{
FLOAT x, y, z, rhw; // the transformed, 3D position for the vertex
DWORD color; // the vertex color
};
// Define the vertices to be used in the buffer
CUSTOMVERTEX g_Vertices [ ] =
{
{320.0f, 50.0f, 0.5f, 1.0f, D3DCOLOR_ARGB (0, 255, 0, 0),},
{250.0f, 400.0f, 0.5f, 1.0f, D3DCOLOR_ARGB (0, 0, 255, 0),},
{50.0f, 400.0f, 0.5f, 1.0f, D3DCOLOR_ARGB (0, 0, 0, 255),},
};
// Copy the vertices into the vertex buffer
memcpy( pVertices, g_Vertices, sizeof( g_Vertices ) );
The sample first declares the CUSTOMVERTEX structure. As mentioned before, this structure
takes a position vertex as well as a color component. Next, an array of vertices is created.
The array, referred to by the g_Vertices variable, holds the vertices to be copied into the
buffer. Finally, a call to memcpy is made to copy the vertices into the buffer. The first parameter
to memcpy, pVertices, refers to the void pointer that was created during the call to Lock.
Unlocking the Vertex Buffer
After the vertices have been copied into the buffer, you must unlock the buffer. Unlocking
the buffer allows Direct3D to continue processing normally. You can unlock the buffer
through the Unlock function, defined here:
HRESULT Unlock (VOID);
The Unlock function requires no parameters and returns the value of D3D_OK on success.
After the vertex buffer is filled with vertices, it’s ready to be drawn to the screen.
The SetupVB function that follows takes all the steps from earlier and places them in an
easy-to-use function.
// variable to hold the newly created vertex buffer
LPDIRECT3DVERTEXBUFFER9 g_pVB = NULL;
/******************************************************************************
* SetupVB
* Creates and fills the vertex buffer
******************************************************************************/
HRESULT SetupVB()
{
HRESULT hr;
// Initialize three vertices for rendering a triangle
CUSTOMVERTEX g_Vertices[] =
{
{320.0f, 50.0f, 0.5f, 1.0f, D3DCOLOR_ARGB (0, 255, 0, 0), },
{250.0f, 400.0f, 0.5f, 1.0f, D3DCOLOR_ARGB (0, 0, 255, 0), },
{50.0f, 400.0f, 0.5f, 1.0f, D3DCOLOR_ARGB (0, 0, 0, 255), },
};
// Create the vertex buffer
hr = pd3dDevice->CreateVertexBuffer(
3*sizeof(CUSTOMVERTEX),
0,
D3DFVF_XYZRHW|D3DFVF_DIFFUSE,
D3DPOOL_DEFAULT,
&g_pVB,
NULL );
// Check to make sure that the vertex buffer was
// created successfully
if FAILED ( hr )
return NULL;
VOID* pVertices;
// Lock the vertex buffer
hr = g_pVB->Lock( 0, sizeof(g_Vertices), (void**)&pVertices, 0 );
// Check to make sure the lock was successful
if FAILED (hr)
return E_FAIL;
// Copy the vertices into the buffer
memcpy( pVertices, g_Vertices, sizeof(g_Vertices) );
// Unlock the vertex buffer
g_pVB->Unlock();
return S_OK;
}
The SetupVB function requires that a variable to hold the vertex buffer is defined outside
the scope of this function. The variable g_pVB refers to this variable. If the vertex buffer is
created and filled successfully, the SetupVB function returns the HRESULT value of S_OK.
Drawing the Contents of the Buffer
Now that you’ve spent all this time creating the vertex buffer and filling it with vertices,
you’re probably wondering when you get to see something on the screen. Well, rendering
the vertices within the vertex buffer requires three steps. The first step is setting the stream
source, followed by configuring the vertex shader, and then finally drawing the vertices to
the screen. These steps are explained in detail in the following sections.
Setting the Stream Source
Direct3D streams are arrays of component data that consist of multiple elements. The vertex
buffer you created earlier is an example of such a stream. Before Direct3D can render
a vertex buffer, you must associate the buffer with a data stream. This is accomplished
with the function SetStreamSource, defined here:
HRESULT SetStreamSource(
UINT StreamNumber,
IDirect3DVertexBuffer9 *pStreamData,
UINT OffsetInBytes,
UINT Stride
);
SetStreamSource requires four parameters.
- StreamNumber. The number of the data stream. If you have created only one vertex
buffer, this parameter is 0.
- pStreamData. The pointer to the variable that contains the vertex buffer.
- OffsetInBytes. The number of bytes from the start of the buffer where the vertex
data is stored. This value is usually 0.
- Stride. The size of each vertex structure within the buffer.
An example call to SetStreamSource is shown next:
pd3dDevice->SetStreamSource ( 0, buffer, 0, sizeof(CUSTOMVERTEX) );
In this function call to SetStreamSource, the first parameter representing the stream number
is set to 0. The second parameter must be a valid pointer to a properly created vertex
buffer. The third parameter is set to 0, telling Direct3D to start at the beginning of the
stream. The final parameter is the stride of the stream. This is set to the size in bytes of the
CUSTOMVERTEX structure. The sizeof function calculates the number of bytes.
Setting the Vertex Shader
After you set the source for the stream, you must set the vertex shader. The vertex shader
tells Direct3D which types of shading to apply. The SetFVF function, defined next, sets up
Direct3D to use a fixed vertex function format.
HRESULT SetFVF(
DWORD FVF
);
The SetFVF function requires only one parameter specified by the variable FVF. The FVF
parameter accepts a value of type D3DFVF.
The following code sample shows how SetFVF is used.
HRESULT hr;
hr = pd3dDevice->SetFVF (D3DFVF_XYZRHW | D3DFVF_DIFFUSE);
// Check the return code to verify that SetFVF completed successfully
if FAILED (hr)
return false;
This code sample passes the values D3DFVF_XYZRHW and D3DFVF_DIFFUSE as the parameter to
SetFVF. As you’ll recall, when the CUSTOMVERTEX structure was set up, it used these two values
when creating the vertex buffer.
You must have already created a valid Direct3D device. It is referred to by the pd3dDevice
variable.
Rendering the Vertex Buffer
Now that you have created the stream and associated it with the vertex buffer, you can render
the vertices to the screen. The function needed to do this is DrawPrimitive, defined next.
The DrawPrimitive function continues through the vertex buffer and renders its data to the
screen.
HRESULT DrawPrimitive(
D3DPRIMITIVETYPE PrimitiveType,
UINT StartVertex,
UINT PrimitiveCount
);
The DrawPrimitive function requires three parameters:
- PrimitiveType. The type of primitive to draw using the vertices within the stream
- StartVertex. The number of the first vertex in the stream
- PrimitiveCount. The number of primitives to render
The PrimitiveType parameter can be any of these enumerated values:
- D3DPT_POINTLIST. A series of individual, unconnected points
- D3DPT_LINELIST. Isolated lines
- D3DPT_LINESTRIP. A series of lines connected by a single vertex
- D3DPT_TRIANGLELIST. Isolated triangles consisting of three vertices
- D3DPT_TRIANGLESTRIP. A series of connected triangles where only one vertex is
required for the definition of each additional triangle
- D3DPT_TRIANGLEFAN. A series of connected triangles that share a common vertex
The following code segment shows a call to DrawPrimitive using a triangle strip as the
primitive type.
HRESULT hr;
// Call DrawPrimitive
hr = pd3dDevice->DrawPrimitive( D3DPT_TRIANGLESTRIP, 0, 1 );
// Check the return code to verify that the function was successful
if FAILED (hr)
return false;
The previous code sample tells Direct3D to render the vertices in the vertex buffer using
a triangle strip described using the D3DPT_TRIANGLESTRIP type as the first parameter. The second
parameter is set to 0, meaning that DrawPrimitive should start with the first vertex in
the buffer. The last parameter is set to 1 because there were only enough vertices defined
to create a single triangle.
A valid Direct3D device must exist. It is referred to by the pd3dDevice variable.
The full source for creating and rendering a vertex buffer is available in the
chapter4\example1 directory on the CD-ROM.
Figure 4.8 shows the drawing of a single colored triangle.
The output of Example 1.
Rendering a Scene
Before you can render 3D primitives, you must prepare Direct3D to render. The BeginScene
function tells Direct3D that rendering is about to take place. Using the BeginScene function,
Direct3D makes sure that the rendering surfaces are valid and ready. If the BeginScene
function fails, your code should skip making rendering calls.
After rendering is done, you need to call the EndScene function. The EndScene function tells
Direct3D that you are finished making rendering calls and the scene is ready to be presented
to the back buffer.
The code that follows confirms the return codes from BeginScene and EndScene.
HRESULT hr;
if ( SUCCEEDED( pDevice->BeginScene( ) ) )
{
// Render primitives only if the scene
// starts successfully
// Close the scene
hr = pDevice->EndScene( );
if ( FAILED ( hr ) )
return hr;
}
The previous code confirms that the call to BeginScene is successful before allowing rendering
to take place using the SUCCEEDED macro around the call. When rendering is complete,
you call the EndScene function.
The next code sample shows what an example render function might look like.
/******************************************************************************
* render
******************************************************************************/
void render()
{
// Clear the back buffer to black
pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET,
D3DCOLOR_XRGB(0,0,0), 1.0f, 0 );
// Tell Direct3D to begin the scene
pd3dDevice->BeginScene();
// Draw the contents of the vertex buffer
// Set the data stream first
pd3dDevice->SetStreamSource( 0, buffer, 0, sizeof(CUSTOMVERTEX) );
// Set the Vertex format for the stream next
pd3dDevice->SetFVF( D3DFVF_CUSTOMVERTEX );
// Draw the vertices within the buffer using triangle strips
pd3dDevice->DrawPrimitive( D3DPT_TRIANGLESTRIP, 0, 1 );
// Tell Direct3D that drawing is complete
pd3dDevice->EndScene();
// copies the back buffer to the screen
pd3dDevice->Present( NULL, NULL, NULL, NULL );
}
The render function takes all these steps and combines then into a single function. The
pd3dDevice variable represents a valid Direct3D device created outside this function.
Primitive Types
Earlier, you had the option of setting the primitive type that DrawPrimitive would use to
render the vertices within the vertex buffer. For the purpose of the previous example, I
chose a triangle strip for its speed and ability to add additional triangles easily. This section
explains in a little more detail the differences among the available primitive types.
Point List
A point list consists of a series of points that are not connected in any way. Figure 4.9
shows a grid containing four distinct points. Each point is defined using X, Y, and Z coordinates.
For example, the top-left point would be defined as (1, 6, 0).
Line List
A line list consists of lines constructed by two points, one at each end. The lines within a
line list are not connected. Figure 4.10 shows two lines rendered using a line list. This particular
line list is constructed from four vertices. The line on the left is formed using (-6,
5, 0) for the upper coordinate and (-4, 2, 0) for the bottom coordinate.
Line Strip
A line strip is a series of connected lines in which each additional line is defined by a
single vertex. Each vertex in the line strip is connected to the previous vertex for a line.
Figure 4.11 shows how a line list is constructed and rendered. The line list in this figure is
constructed using a series of six vertices, creating five lines.
Triangle List
A triangle list contains triangles that are not connected in any way and can appear anywhere
within your world. Figure 4.12 shows two individual triangles constructed from six
vertices. Each triangle requires three vertices to construct a complete triangle.
Figure 4.9 An example of rendered points using a point list. Figure 4.10 Lines rendered using a line list.
Figure 4.11 Lines rendered using a line strip. Figure 4.12 Triangles rendered using atriangle list.
Triangle Strip
A triangle strip is a series of triangles connected to one another in which only one vertex
is required to define each additional triangle. Figure 4.13 shows four triangles created
using only six vertices.
Triangle strips are constructed first by creating three vertices to define the first triangle. If
an additional vertex is defined, lines are drawn between the two previously created vertices,
forming another triangle. In Figure 4.13, the order of the vertices’ creation is shown.
Figure 4.13 Triangles rendered using a
triangle strip.
Triangle Fan
A triangle fan is a series of triangles that share a common vertex. After the first triangle is
created, each additional vertex creates another triangle with one of its points being the
first vertex defined.
Figure 4.14 shows how a triangle fan consisting of three triangles is created using only five
vertices. The order of the vertices controls what the triangle fan looks like. Figure 4.14
shows the order of the vertices’ creation needed to construct the displayed fan.
Figure 4.14 Triangles rendered using a
triangle fan.
|