DirectX 游戏编程之3D空间,极点缓存及索引缓存

DirectX 游戏编程之3D空间,顶点缓存及索引缓存

3D物体的模型表示

场景(scene)是物体或模型的的集合,所有物体都可以使用三角形网格逼近表示:

DirectX 游戏编程之3D空间,极点缓存及索引缓存

三角形(triangle)是构建物体模型的基本单元,也是基本图元之一(Primitive,包括点和线段)

而构成三角形的则是顶点(vertex ),顶点在3D系统中描述一个点,三角形由三个点构成

基本图元(Primitives)

3D空间图形的基本组成元素就是图元(Primitive),不仅仅是三角形,包括以下:

(以下图形在z=0平面)

1点列表(Point Lists):由系列顶点组成,每个点都是独立的,散列的

2线列表(Line Lists):每一组顶点构成一条独立分隔的线段,通常用做3D网格,雨点,路标线等物体建模

3线条纹(Line Strips):类似于线列表,但是每两条线又被一条线段连接,常用于wire-frame物体,如地形,草丛

DirectX 游戏编程之3D空间,极点缓存及索引缓存

4三角形列表(Triangle Lists):每三个顶点一组构成一个独立的三角形

5三角形条纹(Triangle Strips):三角形间相互连接

DirectX 游戏编程之3D空间,极点缓存及索引缓存

6三角形扇面(Triangle Fans):所有三角形拥有一个共同顶点

DirectX 游戏编程之3D空间,极点缓存及索引缓存

顶点缓存与索引缓存

D3D使用灵活顶点格式(Flexible Vertex Format,FVF),定义一个顶点的位置及其他信息,包括颜色、法线等,通过组合不同顶点格式,以令我们可以自定义自己的顶点格式

#define CUSTOMFVF (D3DFVF_XYZRHW | D3DFVF_DIFFUSE)
struct CUSTOMVERTEX
{
    FLOAT x, y, z, rhw;    // from the D3DFVF_XYZRHW flag
    DWORD color;    // from the D3DFVF_DIFFUSE flag
}

几个重要的顶点格式:

D3DFVF_XYZ

指定x,y,z坐标位置,同时它是Untransformed的,表明未经过顶点转换到屏幕坐标

D3DFVF_XYZRHW

指定xyz坐标,不能与D3DFVF_XYZ共用,其是transformed,表明可以直接使用屏幕坐标而不需要经过世界,取景,投影转换等显示在屏幕上。

D3DFVF_DIFFUSE

顶点包含32bit的漫反射颜色

D3DFVF_SPECULAR

包含32bit镜面反射颜色

D3DFVF_NORMAL

包含法线向量(x,y,z)

D3DFVF_TEX0

-D3DFVF_TEX8

包含纹理坐标(u,v)


一 顶点缓存的创建使用顶点缓存与索引缓存
HRESULT CreateVertexBuffer(
  UINT Length,
  DWORD Usage,
  DWORD FVF,
  D3DPOOL Pool,
  IDirect3DVertexBuffer9 **ppVertexBuffer,
  HANDLE *pSharedHandle
);

Length:为缓存分配的字节数,为顶点个数num*sizeof(Vertex)

Usage:缓存的附加属性设置,可以为以下值

D3DUSAGE_DYNAMIC 将缓存设定为动态。

D3DUSAGE_WRITEONLY 缓存设定为只写

FVF:设置灵活顶点格式

Pool:内存池

ppVertexBuffer:顶点缓存的指针

pSharedHandle:保留,设为0

// 三角形的顶点缓存创建
IDirect3DDevice9* Device = 0;
IDirect3DVertexBuffer9* Triangle = 0; // vertex buffer to store
                                      // our triangle data.
Device->CreateVertexBuffer(
		3 * sizeof(Vertex), // size in bytes
		D3DUSAGE_WRITEONLY, // flags,write only
		D3DFVF_XYZ,        // vertex format
		D3DPOOL_MANAGED,    // managed memory pool
		&Triangle,          // return create vertex buffer
		0);                 // not used - set to 0

索引缓存的创建:

HRESULT CreateIndexBuffer(
  UINT Length,
  DWORD Usage,
  D3DFORMAT Format,
  D3DPOOL Pool,
  IDirect3DIndexBuffer9 **ppIndexBuffer,
  HANDLE *pSharedHandle
);

二 访问缓存内容

为了访问顶点缓存数据,先获取缓存的内存指针

HRESULT IDirect3DVertexBuffer9::Lock(
  UINT OffsetToLock,
  UINT SizeToLock,
  VOID **ppbData,
  DWORD Flags
);

OffsetToLock:缓存开始到锁定位置的偏移,单位为字节bytes

SizeToLock:锁定字节数。OffsetToLock,SizeToLock都为0,表示锁定全部缓存。

ppbData:指向锁定开始位置的指针

Flags:锁定方式

// 顶点数据格式
struct Vertex
{
	Vertex(){}
	Vertex(float x, float y, float z)
	{
		_x = x;	 _y = y;  _z = z;
	}
	float _x, _y, _z;
};
Vertex* vertices;
Triangle->Lock(0, 0, (void**)&vertices, 0);  // 锁定全部缓存
// 设置顶点数据
vertices[0] = Vertex(-1.0f, 0.0f, 2.0f);
vertices[1] = Vertex( 0.0f, 1.0f, 2.0f);
vertices[2] = Vertex( 1.0f, 0.0f, 2.0f);
// Lock 与 Unlock 成对使用
Triangle->Unlock();

获取顶点缓存或索引缓存信息的方法

HRESULT IDirect3DVertexBuffer9::GetDesc(
  D3DVERTEXBUFFER_DESC *pDesc
);

HRESULT IDirect3DIndexBuffer9::GetDesc(
  D3DINDEXBUFFER_DESC *pDesc
);

四 设置绘制状态(rendering state)

HRESULT IDirect3DDevice9::SetRenderState(
  D3DRENDERSTATETYPE State,
  DWORD Value
);

我们设置绘制物体为线框模式,原默认值为:D3DFILL_SOLID

SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME);

五 绘制设置

1数据流关联,将顶点缓存设备数据流进行关联

HRESULT IDirect3DDevice9::SetStreamSource(
  UINT StreamNumber,
  IDirect3DVertexBuffer9 *pStreamData,
  UINT OffsetInBytes,
  UINT Stride
);

StreamNumber:在使用多个数据流的情况下编号辨识。

pStreamData:IDirect3DVertexBuffer9顶点缓存指针。

OffsetInBytes:顶点数据开始的数据流偏移,以字节为单位。

Stride:顶点缓存每个元素大小,字节为单位。

SetStreamSource(0, Triangle, 0, sizeof(Vertex));

2设置绘制顶点格式。

HRESULT IDirect3DDevice9::SetFVF(
  DWORD FVF
);
例如:

SetFVF(D3DFVF_XYZ);

3如果是索引缓存,需要对绘制的索引进行设置,索引缓存与绘制数据流进行关联。

HRESULT IDirect3DDevice9::SetIndices(
  IDirect3DIndexBuffer9 *pIndexData
);

六 顶点缓存或的绘制

以下为顶点缓存绘制图元函数

HRESULT IDirect3DDevice9::DrawPrimitive(
  D3DPRIMITIVETYPE PrimitiveType,
  UINT StartVertex,
  UINT PrimitiveCount
);

PrimitiveType:图元类型,如绘制三角形时,使用D3DPT_TRIANGLELIST。

StartVertex:顶点数据流中开始绘制元素,以便可以部分绘制。

PrimitiveCount:绘制图元数量。

如绘制一个三角形图元,参考代码中顶点位置:从顶点缓存的第一个元素开始绘制,绘制一个三角形图元。

DrawPrimitive(D3DPT_TRIANGLELIST, 0, 1);

顶点缓存的绘制:将顶点元素写入缓存中,绘制是在缓存中根据索引取出顶点绘制。

绘制索引缓存图元函数

HRESULT DrawIndexedPrimitive(
  D3DPRIMITIVETYPE Type,
  INT BaseVertexIndex,
  UINT MinIndex,
  UINT NumVertices,
  UINT StartIndex,
  UINT PrimitiveCount
);

Type:绘制图元类型

BaseVertexIndex:顶点缓存中第几个元素为基位置,表示为索引从该处开始,为索引的最小值,如BaseVertexIndex:0,BaseVertexIndex+1:1,。

MinIndex:允许的最小索引值,一般为0,这样的索引列表为0,1,2,3,4…

NumVertices:本次绘制的顶点数目,并非索引数目,矩形的NumVertices为4,并非6。(矩形由两个三角形基本图元组成,共6个索引)

StartIndex:绘制索引的开始位置。

PrimitiveCount:绘制图元总数。

 

使用顶点缓存如绘制一个正方体,顶点缓存内容:

struct Vertex
{
	Vertex(){}
	Vertex(float x, float y, float z)
	{
		_x = x;  _y = y;  _z = z;
	}
	float _x, _y, _z;
};
const DWORD Vertex::FVF = D3DFVF_XYZ;
IDirect3DDevice9* Device = 0;
IDirect3DVertexBuffer9* VB = 0;
IDirect3DIndexBuffer9*  IB = 0;
// 创建顶点缓存、索引缓存
Device->CreateVertexBuffer(
	8 * sizeof(Vertex), 
	D3DUSAGE_WRITEONLY,
	Vertex::FVF,
	D3DPOOL_MANAGED,
	&VB,
	0);
Device->CreateIndexBuffer(
	36 * sizeof(WORD),
	D3DUSAGE_WRITEONLY,
	D3DFMT_INDEX16,
	D3DPOOL_MANAGED,
	&IB,
	0);
// 定义顶点数据
Vertex* vertices;
VB->Lock(0, 0, (void**)&vertices, 0);

// vertices of a unit cube
vertices[0] = Vertex(-1.0f, -1.0f, -1.0f);
vertices[1] = Vertex(-1.0f,  1.0f, -1.0f);
vertices[2] = Vertex( 1.0f,  1.0f, -1.0f);
vertices[3] = Vertex( 1.0f, -1.0f, -1.0f);
vertices[4] = Vertex(-1.0f, -1.0f,  1.0f);
vertices[5] = Vertex(-1.0f,  1.0f,  1.0f);
vertices[6] = Vertex( 1.0f,  1.0f,  1.0f);
vertices[7] = Vertex( 1.0f, -1.0f,  1.0f);

VB->Unlock();


// 定义索引数据
WORD* indices = 0;
IB->Lock(0, 0, (void**)&indices, 0);

// front side
indices[0]  = 0; indices[1]  = 1; indices[2]  = 2;
indices[3]  = 0; indices[4]  = 2; indices[5]  = 3;

// back side
indices[6]  = 4; indices[7]  = 6; indices[8]  = 5;
indices[9]  = 4; indices[10] = 7; indices[11] = 6;

// left side
indices[12] = 4; indices[13] = 5; indices[14] = 1;
indices[15] = 4; indices[16] = 1; indices[17] = 0;

// right side
indices[18] = 3; indices[19] = 2; indices[20] = 6;
indices[21] = 3; indices[22] = 6; indices[23] = 7;

// top
indices[24] = 1; indices[25] = 5; indices[26] = 6;
indices[27] = 1; indices[28] = 6; indices[29] = 2;

// bottom
indices[30] = 4; indices[31] = 0; indices[32] = 3;
indices[33] = 4; indices[34] = 3; indices[35] = 7;

IB->Unlock();


// 绘制设置
Device->SetStreamSource(0, VB, 0, sizeof(Vertex));
Device->SetIndices(IB);
Device->SetFVF(Vertex::FVF);

// 绘制
Device->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, 8, 0, 12);

各参数分别为:以三角形为图元;顶点缓存中第一个元素为索引开始;索引的最小值为0,索引顶点的第一个元素为索引0;正方体的顶点数目为8个;从索引列表的第一个索引开始绘制;共12个三角形图元。

注意:所有的绘制函数必须在 IDirect3DDevice9::BeginScene/ IDirect3DDevice9::EndScene 方法之间调用。


REF:

directxtutorial.com

《DIRECTX.9.0.3D游戏开发编程基础(Inroduction.to.3D.GAME.Programming.with.DirectX.9.0)》