这里只有一组顶点数据。多组顶点数据表示一个顶点的多个数据项。
Chapter 8: Drawing in Direct3D — Part II Overview
8.5 D3DX Geometric Objects
Simple objects like cubes are easy to construct by hand. A grid can be easily computed procedurally (i.e., by a step-by-step procedure that tells the computer how to generate the geometry). Other geometric objects can also be computed procedurally, such as cylinders, spheres, and toruses, for which the D3DX library conveniently provides methods. Specifically, the D3DX library provides the following six mesh creation functions:
D3DXCreateBox D3DXCreateSphere D3DXCreateCylinder D3DXOeateTorus D3DXOeateTeapot D3DXOeatePolygon
All six of these functions are similar and utilize the ID3DXMesh interface, which we discuss in a later chapter. For now, we treat the interface as a black box (i.e., we use it without knowing how it works "under the hood"). For example, the D3DXCreateCylinder function is prototyped as follows:
HRESULT WINAPI D3DXCreateCylinder(LPDIRECT3DDEVICE9 pDevice, FLOAT Radius1, FLOAT Radius2, FLOAT Length, UINT Slices, UINT Stacks, LPD3DXMESH *ppMesh, LPD3DXBUFFER *ppAdjacency);
The first parameter is a pointer to the rendering device. Figure 8.8 describes parameters two through five.
Figure 8.8: D3DXCreateCylinder parameters two through five.
Observe that the cylinder is built along the z-axis. The sixth parameter returns a pointer to an ID3DXMesh interface, which stores the cylinder geometry. We ignore the seventh parameter for now and specify null; adjacency info is discussed in a later chapter. Here is an example call:
ID3DXMesh* mCylinder;
HR(D3DXCreateCylinder(gd3dDevice, 1.0f, 1.0f, 6.0f, 20, 20, &mCylinder, 0));
Remark By setting one of the radii to zero, you can make a cone with this function.
To draw the cylinder, we write:
HR(mCylinder - >DrawSubset(0));
Because ID3DXMesh objects are COM interfaces, we must release them when done:
ReleaseCOM(mCylinder);
Figure 8.9 shows a screenshot of the demo we are making, illustrating the D3DXCreateCylinder and D3DXCreateSphere functions. The key code of this demo (called MeshDemo) is contained in the methods MeshDemo::drawCylinders and MeshDemo::drawSpheres. We'll explain MeshDemo::drawCylinders here;
MeshDemo::drawSpheres is essentially analogous.
Figure 8.9: Screenshot of the Mesh demo.
void MeshDemo::drawCylinders () {
D3DXMATRIX T, R;
D3DXMatrixRotationX(&R, D3DX_PI*0.5f);
for(int z = - 30; z <= 30; z+= 10) {
D3DXMatrixTranslation(&T, - 10.0f, 3.0f, (float)z);
HR(mFX - >SetMatrix(mhWVP, &(R*T*mView*mProj)));
HR(mFX - >CommitChanges());
HR(mCylinder - >DrawSubset(0));
D3DXMatnxTranslation(&T, 10.0f, 3.0f, (float)z);
HR(mFX - >SetMatrix(mhWVP, &(R*T*mView*mProj)));
HR(mFX - >CommitChanges());
HR(mCylinder - >DrawSubset(0));
} }
The first thing we do is instantiate two matrices — a translation and a rotation matrix. We need a rotation matrix because the cylinders are built along the z-axis in local space, and we want them to be aligned with the world space's yaxis; therefore, we need to rotate the cylinders 90° about the xaxis. Then we loop from z = -30 to +-30 by increments of 10. For each iteration, we draw a cylinder twice at position (-10, 3, z) and (+10, 3, z). The cylinder is positioned to these locations by a translation matrix.
Observe that every time we change the matrix effect parameter (so the geometry is drawn with the correct matrix applied), we must call ID3DXEffect::CommitChanges, which updates the effect parameters based on ID3DXEffect::Set* calls.
8.6 Summary
A vertex shader is a program executed on the graphics card's GPU (graphics processing unit) that operates on vertices. The responsibilities of a vertex shader include the world, view, and projection transformations; vertex lighting; and any other calculations that need to be done at the vertex level. Data computed at the vertex level can be interpolated and input into data at the pixel level.
A pixel shader is a program executed on the graphics card's GPU that operates on pixel fragments (candidates that can become screen pixels if they pass various operations that can prevent a pixel from being written, such as the alpha test, stencil test, and depth buffer test). The primary
responsibility of a pixel shader includes generating a fragment color based on interpolated vertex colors, texture data, pixel lighting, fog, and any other calculations that need to be done at the pixel level.
A Direct3D effect encapsulates the code that describes how to render 3D geometry in a particular way. An effect consists of a technique, which consists of one or more passes. An effect may implement several techniques that all implement the same effect but in different ways. Each
implementation will utilize the capabilities of a specific generation of hardware. Thus, the application can choose the technique that is most appropriate for the target hardware. Note that because an effect is typically written in an external file, it can be modified without having to recompile the application source code.
A technique consists of one or more rendering passes. A rendering pass consists of the device states and shaders used to render the geometry for that particular pass. Multiple rendering passes are necessary because some effects require the same geometry to be rendered several times, each time with different device states and/or shaders, and then the result of each pass is combined to form the net technique. On a performance note, you should try to avoid multi-pass techniques if possible, as rendering the same geometry multiple times is obviously more expensive.
Using the D3DXOeate* functions, we can create the geometry of more complex 3D objects such as spheres, cylinders, and teapots.
8.7 Exercises
1. Rewrite the Cube demo from Chapter 7 using the programmable pipeline with transform.fx.
2. As you learn in the next chapter, we can represent the color red by the 4D color vector (1, 0, 0, 1). Modify the Triangle Grid demo so that the wireframe lines are drawn with red instead of black.
3. Use D3DXCreateCylinder to draw a cone; see Figure 8.10a.
4. Use D3DXCreateTeapot to draw a teapot; see Figure 8.10b. The prototype of this function is given by:
HRESULT WINAPI D3DXCreateTeapot(
LPDIRECT3DDEVICE9 pDevice, LPD3DXMESH *ppMesh,
LPD3DXBUFFER *ppAdjacency);
5. Use D3DXCreateTorus to draw a torus; see Figure 8.10c. The prototype of this function is given by:
HRESULT WINAPI D3DXCreateTorus(
LPDIRECT3DDEVICE9 pDevice, FLOAT InnerRadius,
FLOAT OuterRadius, UINT Sides, UINT Rings,
LPD3DXMESH *ppMesh,
LPD3DXBUFFER *ppAdjacency);
Figure 8.10: Output for exercises 3, 4, and 5.