Direct3D提供了多种渲染状态,它影响几何物体怎样被渲染。渲染状态有默认值,因此假如你的应用程序需要不同于默认设置的渲染时,你仅仅改变它即可。一种渲染效果会一直起作用,直到你下一次改变渲染状态为止。为了设置一个渲染状态,我们使用下面的方法:
Sets a single device render-state parameter.
HRESULT SetRenderState(
D3DRENDERSTATETYPE State,
DWORD Value
);
Parameters
-
State
- [in] Device state variable that is being modified. This parameter can be any member of the D3DRENDERSTATETYPE enumerated type. Value
- [in] New value for the device render state to be set. The meaning of this parameter is dependent on the value specified for State. For example, if State were D3DRS_SHADEMODE, the second parameter would be one member of the D3DSHADEMODE enumerated type.
Return Values
If the method succeeds, the return value is D3D_OK. D3DERR_INVALIDCALL is returned if one of the arguments is invalid.
例如,在下面的例子中我们将使用线框模式渲染我们的物体。因此,我们设置如下的渲染状态:
_device->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME);
注意:查看DirectX SDK中关于D3DRENDERSTATETYPE的信息。其中详细介绍了所有的渲染状态。
3.3 绘制准备
一旦我们创建好一个顶点缓存以及一个索引缓存(可选的)后,我们就为渲染其中的内容准备得差不多了,但是在渲染前我们还有3个步骤必须先做。
1、 设置资源流。设置资源流与一个顶点缓存挂钩,此流就是一个流入渲染管线的几何信息的流。
下面的方法是用于设置一个资源流:
HRESULT IDirect3DDevice9::SetStreamSource(
UINT StreamNumber,
IDirect3DVertexBuffer9* pStreamData,
UINT OffsetInBytes,
UINT Stride
);
StreamNumber——确定我们的顶点缓存与哪一个资源流挂钩。我们不使用多重流;因此我们总是使用0号流。
pStreamData——一个指向我们想与流挂钩的那个顶点缓存的指针。
OffsetInBytes——相对流开始处的偏移量。以字节为单位,它指定被填入渲染管线的顶点数据的开始位置。通过检查D3DCAPS9结构中的D3DDEVCAPS2_STREAMOFFSET标志,假如你的设备支持,那么这个参数就有一些非0值。
Stride——我们在顶点缓存中操作的每个部分的流的字节大小。
例如,假设vb是一个已经填充了顶点信息的顶点缓存:
_device->SetStreamSource( 0, vb, 0, sizeof( Vertex ) );
2、 设置索引缓存。假如我们使用了索引缓存,我们必须设置后面要用于绘制操作的索引缓存。每次我们只能使用一个索引缓存;因此假如你需要用一个不同的索引缓存绘制一个物体时,你必须转换到另一个上。下面的代码设置一个索引缓存:
_device->SetIndices( _ib ); // 传递一个索引缓存指针的拷贝
3.4用顶点/索引缓存绘制
在我们创建好顶点/索引缓存以及做好准备工作以后,我们就能绘制我们的几何物体了。这是通过使用DrawPrimitive或者DrawIndexedPrimitive传送几何信息到达渲染管线。这些方法从顶点流中获得顶点信息以及从索引缓存中获得索引信息。
3.4.1 IDirect3DDevice9::DrawPrimitive
这个方法不使用索引信息绘制图元。
HRESULT IDirect3DDevice9::DrawPrimitive(
D3DPRIMITIVETYPE PrimitiveType,
UINT StartVertex,
UINT PrimitiveCount
);
PrimitiveType——我们绘制的图元类型。比如,我们能绘制点和线以及三角形。以后我们使用三角形,用D3DPT_TRIANGLELIST参数。
StartVertex——索引到在顶点流中的一个元素。设置渲染顶点中的开始点。这个参数给予我们一定的机动性,可以绘制一个顶点缓存中的某部分。
PrimitiveCount——绘制图元的个数。
例子:
// 绘制4个三角形
_device->DrawPrimitive( D3DPT_TRIANGLELIST, 0, 4);
Renders a sequence of nonindexed, geometric primitives of the specified type from the current set of data input streams.
HRESULT DrawPrimitive(
D3DPRIMITIVETYPE PrimitiveType,
UINT StartVertex,
UINT PrimitiveCount
);
Parameters
-
PrimitiveType
- [in] Member of the D3DPRIMITIVETYPE enumerated type, describing the type of primitive to render. StartVertex
- [in] Index of the first vertex to load. Beginning at StartVertex the correct number of vertices will be read out of the vertex buffer. PrimitiveCount
- [in] Number of primitives to render. The maximum number of primitives allowed is determined by checking the MaxPrimitiveCount member of the D3DCAPS9 structure. PrimitiveCount is the number of primitives as determined by the primitive type. If it is a line list, each primitive has two vertices. If it is a triangle list, each primitive has three vertices.
Return Values
If the method succeeds, the return value is D3D_OK. If the method fails, the return value can be D3DERR_INVALIDCALL.
Remarks
When converting a legacy application to Direct3D 9, you must add a call to either IDirect3DDevice9::SetFVF to use the fixed function pipeline, or IDirect3DDevice9::SetVertexDeclaration to use a vertex shader before you make any Draw calls.
Defines the primitives supported by Direct3D.
typedef enum D3DPRIMITIVETYPE
{
D3DPT_POINTLIST = 1,
D3DPT_LINELIST = 2,
D3DPT_LINESTRIP = 3,
D3DPT_TRIANGLELIST = 4,
D3DPT_TRIANGLESTRIP = 5,
D3DPT_TRIANGLEFAN = 6,
D3DPT_FORCE_DWORD = 0x7fffffff,
} D3DPRIMITIVETYPE, *LPD3DPRIMITIVETYPE;
Constants
-
D3DPT_POINTLIST
- Renders the vertices as a collection of isolated points. This value is unsupported for indexed primitives. D3DPT_LINELIST
- Renders the vertices as a list of isolated straight line segments. D3DPT_LINESTRIP
- Renders the vertices as a single polyline. D3DPT_TRIANGLELIST
Renders the specified vertices as a sequence of isolated triangles. Each group of three vertices defines a separate triangle.
Back-face culling is affected by the current winding-order render state. D3DPT_TRIANGLESTRIP Renders the vertices as a triangle strip. The backface-culling flag is automatically flipped on even-numbered triangles. D3DPT_TRIANGLEFAN Renders the vertices as a triangle fan. D3DPT_FORCE_DWORD Forces this enumeration to compile to 32 bits in size. Without this value, some compilers would allow this enumeration to compile to a size other than 32 bits. This value is not used.Remarks
Using Triangle Strips (Direct3D 9) or Triangle Fans (Direct3D 9) is often more efficient than using triangle lists because fewer vertices are duplicated.
3.4.2 IDirect3DDevice9::DrawIndexedPrimitive
这个方法使用索引信息来绘制图元。
HRESULT IDirect3DDevice9::DrawIndexedPrimitive(
D3DPRIMITIVETYPE Type,
INT BaseVertexIndex,
UINT MinIndex,
UINT NumVertices,
UINT StartIndex,
UINT PrimitiveCount
);
Type——我们绘制的图元类型。比如,我们能绘制点和线以及三角形。以后我们使用三角形,用D3DPT_TRIANGLELIST参数。
BaseVertexIndex——一个基本数字,在调用中用它去加上索引。参看下面的说明。
MinIndex——将被引用的最小索引值。
NumVertices——在此调用中将被引用的顶点数。
StartIndex——索引到索引缓存中的某个位置,它标记开始渲染的开始索引点。
PrimitiveCount——绘制图元的个数。
例子:
_device->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, 8, 0, 12);
注意:BaseVertexIndex参数需要一些特别的解释。在解释过程中将会用到的图3.2。
在索引缓存中定位顶点相应的也就在顶点缓存中定位了。然而,假设我们想将球,盒子,圆柱体的顶点放置到一个公共的顶点缓存中。对于每一个物体,我们将不得不再计算在公共顶点缓存中的索引。这个新的索引值是通过与一个偏移量相加得到。注意这个偏移量是标准的顶点,而不是字节。
我们需要计算物体在公共顶点缓存中的索引值。Direct3D允许我们通过设置BaseVertexIndex参数得到一个顶点偏移量,随后Direct3D就能利用顶点自身的索引重新计算新的索引。
3.4.3 开始/结束场景
最后一点就是所有绘制方法都必须在IDirect3DDevice9::BeginScene和IDirect3DDevice9::EndScene方法之间被调用。例如,我们将这样写:
_device->BeginScene();
// 绘制场景
_device->DrawPrimitive(...);
_device->EndScene();
3.5 D3DX几何物体
通过在代码中建造每个三角形来建造3D物体是一件非常枯燥的事。幸运的是,D3DX库已经为我们提供了一些方法来产生简单3D物体的网格数据。
D3DX库提供如下6种网格生成函数。
D3DXCreateBox
D3DXCreateSphere
D3DXCreateCylinder
D3DXCreateTeapot
D3DXCreatePolygon
D3DXCreateTorus
这6种函数的使用都很类似,并且使用D3DX网格数据结构ID3DXMesh就象使用ID3DXBuffer接口一样。现在,我们忽视它们的详细信息,只需简单使用它们即可。
HRESULT D3DXCreateTeapot(
LPDIRECT3DDEVICE9 pDevice, // 与mesh关联的设备
LPD3DXMESH* ppMesh, // 返回的mesh
LPD3DXBUFFER* ppAdjacency // 现在设成0
);
一个使用D3DXCreateTeapot函数的例子:
ID3DXMesh* mesh = 0;
D3DXCreateTeapot(_device, &mesh, 0);
一旦生成了网格数据,我们就能使用ID3DXMesh::DrawSubset方法绘制图形了。这个方法有一个参数,它用来识别网格的一个子集。这个网格是通过上面的D3DXCreate*函数中的一个子集创建的,因此可以给这个参数指定0值。一个渲染网格的例子:
_device->BeginScene();
mesh->DrawSubset(0);
_device->EndScene();
使用了网格以后,必须释放(release)它:
mesh->Release();
_mesh = 0;
实例程序:三角形
这是非常简单的应用程序,它示范了在线框模式下怎样创建并渲染一个三角形。
/**************************************************************************************
Renders a triangle in wireframe mode.
Demonstrates vertex buffers, render states, and drawing commands.
**************************************************************************************/
#include "d3dUtility.h"
#pragma warning(disable : 4100)
const int WIDTH = 640;
const int HEIGHT = 480;
IDirect3DDevice9* g_d3d_device = NULL;
IDirect3DVertexBuffer9* g_triangle_vb = NULL;
class cVertex
{
public:
float m_x, m_y, m_z;
cVertex() {}
cVertex(float x, float y, float z)
{
m_x = x;
m_y = y;
m_z = z;
}
};
const DWORD VERTEX_FVF = D3DFVF_XYZ;
bool setup()
{
g_d3d_device->CreateVertexBuffer(3 * sizeof(cVertex), D3DUSAGE_WRITEONLY, VERTEX_FVF,
D3DPOOL_MANAGED, &g_triangle_vb, NULL);
// fill the buffers with the triangle data
cVertex* vertices;
g_triangle_vb->Lock(0, 0, (void**)&vertices, 0);
vertices[0] = cVertex(-1.0f, 0.0f, 2.0f);
vertices[1] = cVertex( 0.0f, 1.0f, 2.0f);
vertices[2] = cVertex( 1.0f, 0.0f, 2.0f);
g_triangle_vb->Unlock();
// set the projection matrix
D3DXMATRIX proj;
D3DXMatrixPerspectiveFovLH(&proj, D3DX_PI * 0.5f, (float)WIDTH/HEIGHT, 1.0f, 1000.0f);
g_d3d_device->SetTransform(D3DTS_PROJECTION, &proj);
// set wireframe mode render state
g_d3d_device->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME);
return true;
}
void cleanup()
{
safe_release<IDirect3DVertexBuffer9*>(g_triangle_vb);
}
bool display(float time_delta)
{
g_d3d_device->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xffffffff, 1.0f, 0);
g_d3d_device->BeginScene();
g_d3d_device->SetStreamSource(0, g_triangle_vb, 0, sizeof(cVertex));
g_d3d_device->SetFVF(VERTEX_FVF);
// draw one triangle
g_d3d_device->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 1);
g_d3d_device->EndScene();
g_d3d_device->Present(NULL, NULL, NULL, NULL);
return true;
}
LRESULT CALLBACK wnd_proc(HWND hwnd, UINT msg, WPARAM word_param, LPARAM long_param)
{
switch(msg)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_KEYDOWN:
if(word_param == VK_ESCAPE)
DestroyWindow(hwnd);
break;
}
return DefWindowProc(hwnd, msg, word_param, long_param);
}
int WINAPI WinMain(HINSTANCE inst, HINSTANCE, PSTR cmd_line, int cmd_show)
{
if(! init_d3d(inst, WIDTH, HEIGHT, true, D3DDEVTYPE_HAL, &g_d3d_device))
{
MessageBox(NULL, "init_d3d() - failed.", 0, MB_OK);
return 0;
}
if(! setup())
{
MessageBox(NULL, "Steup() - failed.", 0, MB_OK);
return 0;
}
enter_msg_loop(display);
cleanup();
g_d3d_device->Release();
return 0;
}
截图: