D3D9学习笔记之顶点缓存与索引缓存

D3D9学习笔记 顶点缓存与索引缓存

顶点缓存与索引缓存可以被放置在显存中,绘制时使用显存中的数据比使用系统内存中的数据要快的多。

顶点缓存与索引缓存在D3D9中的表示:
顶点缓存 IDirect3DVetrexBuffer9
索引缓存 IDirect3DIndexBuffer9

创建顶点缓存和索引缓存:

可以用如下2个方法创建

HRESULT IDirect3DDevice9::CreateVertexBuffer(
	UINT		Length,		//为缓存分配的字节数量
	DWORD		Usage,		//关于如何使用缓存的附加信息
	DWORD 		FVF,		//存储在顶点缓存中顶点的灵活顶点格式
	D3DPOOL		Pool,		//容纳缓存的内存池
	IDirect3DVertexBuffer9** 	ppVertexBuffer,		//接收所创建顶点缓存的指针
	HANDLE* 	pSharedHandle		//不使用 为0
);

HRESULT IDirect3DDevice9::CreateIndexBuffer(
	UINT		Length,		//为缓存分配的字节数量
	DWORD		Usage,		//关于如何使用缓存的附加信息
	D3DFORMAT	Format,		//指定索引的大小D3DFMT_INDEX16或D3DFMT_INDEX32
	D3DPOOL 	Pool,		//容纳缓存的内存池
	IDirect3DIndexBuffer9** ppIndexBuffer,		//用于接收所创建的索引缓存的指针
	HANDLE* pSharedHandle		//不使用 为0
);

上述两个方法大部分参数相同,下面介绍参数:
Length 为缓存分配的字节数,如果想让顶点缓存足够存储8个顶点,该参数需设为8*sizeof(Vertex),其中Vetrex是定义顶点的结构。
Usage 指定关于如何使用缓存的一些附加属性,该值可为0(表面无需附加属性),或是一下标记中的某一个或某种组合。

  • D3DUSAGE_DYNAMIC 将缓存设为动态缓存。(如果需要频繁更新缓存中的内容,该缓存应设为动态的)
  • D3DUSAGE_POINTS 该标记规定缓存将用于存储点图元。该标记仅用于顶点缓存。
  • D3DUSAGE_SOFTWAREPROCESSING 指定软件顶点运算方式。
  • D3DUSAGE_WRITEONLY 规定程序对缓存的操作模式为“只写”。这样驱动程序就可以将缓存放在最适合写操作的内存中。如果该模式下进行读操作将会出错。

FVF 存储在顶点缓存中顶点的灵活顶点格式。
POOL 容纳缓存的内存池。
ppVertexBuffer 用于接收所创建的顶点缓存指针。
pSharedHandle 不使用,该值设为0。
Format 指定索引的大小。设为D3DFMT_INDEX16表示16位索引,设为D3DFMT_INDEX32表示位32位索引。注意,并非所有图像设备都支持32位,如需使用,请检查硬件的性能。
ppIndexBuffer 用于接收所创建的索引缓存的指针。

使用的例子:

//下面例子中,创建了一个可以容纳8个Vertex类型的顶点的静态顶点缓存

IDirect3DVertexBuffer9* vb;//顶点缓存对象
device->CreateVertexBuffer(
	8*sizeof(Vertex),
	0,										//0为静态顶点缓存
	D3DFVF_XYZ,
	D3DPOOL_MANAGED,
	&vb,
	0
);

//创建一个动态缓存的例子,可容纳36个16位的索引

IDirect3DIndexBuffer9* ib;
device->CreateIndexBuffer(
	36*sizeof(WORD),
	D3DUSAGE_DYNAMIC|D3DUSAGE_WRITEONLY,	//动态缓存与只写
	D3DFMT_INDEX16,							//指定索引的大小为16位
	D3DPOOL_MANAGED,
	&ib,
	0
);

访问顶点缓存和索引缓存:

为了访问顶点缓存或索引缓存中的数据,需要先获得指向缓存内部存储区的指针。使用方法Lock来获取。
很重要的一点是,对缓存访问完毕后,必须对缓存进行解锁。
获取拉指向缓存内容的指针后,就可以对其内容进行读写操作。

注意

  • 如果创建顶点缓存或索引缓存时使用拉D3DUSAGE_WRITEONLY标记,便无法对缓存进行读操作,否则会导致读取失败
//以下2个方法参数完全相同
//获取顶点缓存指针
HRESULT IDirect3DVertexBuffer9::Lock(
	UINT OffsetToLock,		//指定偏移量(字节)
	UINT SizeToLock,		//所要锁定的字节数
	BYTE** ppbData,			//指向被锁定的存储区起始位置的指针
	DWORD Flags				//标记描述位,描述锁定的方式
);

//获取索引缓存指针
HRESULT IDirect3DIndexBuffer9::Lock(
	UINT OffsetToLock,
	UINT SizeToLock,
	BYTE** ppbData,
	DWORD Flags
);

参数理解:

  • OffsetToLock 自缓存的起点开始到锁定的位置的偏移量,单位为字节。
  • SizeToLock 所要锁定的字节数。
  • ppbData 指向被锁定的存储区起始位置的指针。
  • Flags 该标记描述拉锁定的方式,可以是0,也可以是下列标记之一或某种组合。
    1:D3DLOCK_DISCARD 该标记仅用于动态缓存。它指示硬件将缓存丢弃,并返回一个重新分配的缓存的指针。该标记十分有用,因为这允许在我们访问新分配的内存时,硬件能够继续使用被丢弃的缓存中的数据进行绘制。
    2:D3DLOCK_NOOVERWRITE 改标记仅用于动态缓存。使用该标记后,数据只能以追加方式写入缓存。即不能覆盖当前用于绘制的存储区中的任何内容。这十分有用,因为它可以保证在往缓存中增加数据时,硬件仍可以继续工作。
    3:D3DLOCK_READONLY 该标记表示对于锁定的缓存只可读而不可写。利用这一点可以作一些内部的优化。

使用示例:

Vertex* vertices;
_vb->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);

_vb->Unlock();		//访问完缓冲区后解锁

获取顶点缓存和索引缓存的信息

有时需要得到有关顶点缓存和索引缓存的信息。下面的例子示范了用于获取这类信息的几种方法。

D3DVERTEXBUFFER_DESC dd;
vertexBuffer->GetDesc(&dd); 		//获取信息放入dd

D3DINDEXBUFFER_DESC dd2;
vertexBuffer->GetDesc(&dd2); 		//获取信息放入dd2

D3DVERTEXBUFFER_DESC 和 D3DINDEXBUFFER_DESC 结构定义如下:

typedef struct _D3DVERTEXBUFFER_DESC{
	D3DFORMAT Format;
	D3DRESOURCETYPE Type;
	DWORD Usage;
	UINT Size;
	DWORD FVF;
}D3DVERTEXBUFFER_DESC;

typedef struct _D3DINDEXBUFFER_DESC{
	D3DFORMAT Format;
	D3DRESOURCETYPE Type;
	DWORD Usage;
	D3DPOOL Pool;
	UINT Size;
}D3DINDEXBUFFER_DESC;

绘制状态

Direct3D封装拉多种绘制状态,这些绘制状态影响几何体的绘制方式。各种绘制状态均具有默认值,所以仅当应用程序需要使用一种不同的绘制状态时,才需要对其进行修改。自指定某种绘制状态起,该状态始终有效,直至该状态被修改为止。
设置绘制状态方法如下:

HRESULT IDirect3DDevice9::SetRenderState(
	D3DRENDERSTATETYPE State,		//要改变的状态
	DWORD Value;					//新状态的值
);

例如,示例中,将用线框模式来绘制物体。所以,应该这样设置绘制状态:

_device->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME);

注意:

  • 查阅DirectX SDK 文档中 D3DRENDERSTATETYPE 相关部分以全面的了解各种绘制状态

绘制的准备工作

一旦我们创建拉顶点缓存以及索引缓存(索引缓存是可选的),我们基本上已经可以对其所存储的内容进行绘制了,但是绘制之前,还有3个步骤需要完成。
1. 指定数据流输入源。将顶点缓存和数据流进行链接,实质上是将几何体的信息传输到绘制流水线中。
下面的方法用于设置顶点数据流的输入源。

HRESULT IDirect3DDevice9::SetStreamSource(
	UINT StreamNumber,
	IDirect3DVertexBuffer9* pStreamData,
	UINT OffsetInBytes,
	UINT Stride
);
  • StreamNumber 标识与顶点缓存建立连接的数据流。如不使用多个流,则总将该参数设为0。
  • pStreamData 指向给定数据流建立链接的顶点缓存的指针。
  • OffsetInBytes 自数据流的起始点算起的一个偏移量,单位为字节,指定了将被传输至绘制流水线的顶点数据的起始位置。如果想将该参数设为某一非零值,务必检查 D3DCAPS9 结构中的 D3DDEVCAPS2_STREAMOFFSET 标记,以判断设备是否支持该功能。
  • Stride 将要链接到数据流的顶点缓存中每个元素的大小,单位为字节。

假设 vb 是一个存储为 Vertex 类型的顶点数据的顶点缓存。

_device->SetStreamSource(0,vb,0,sizeof(Vertex));

2:设置顶点格式。在这里指定后续绘制调用中使用的顶点格式。

_device->SetFVF(D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1);

3:设置索引缓存。如果要使用索引缓存,必须对后续绘制操作中所要使用的索引缓存进行设置。任意时刻只允许使用一个索引缓存,如果要使用不同的索引缓存时,必须进行切换。
下面的代码演示如何设置索引缓存:

_device->SetIndices(_id);		//传递索引缓冲区指针的副本

使用顶点缓存和索引缓存进行绘制

待顶点缓存和索引缓存创建完毕,且准备工作完成之后,我们便可对几何体进行绘制,也就是使用 DrawPrimitive 或 DrawIndexedPrimitive 方法将待绘制几何体的信息通过绘制流水线传输。这两个方法从顶点数据流中获取顶点信息,并从当前设定的索引缓存中提取索引信息。
IDirect3DDevice9::DrawPrimitive
该方法可用于绘制未使用索引信息的图元。

HRESULT IDirect3Ddevice9::DrawPrimitive(
	D3DPRIMITIVETYPE PrimitiveType,
	UINT StartVertex,
	UINT PrimitiveCount
);
  • PrimitiveType 所要绘制的图元类型。例如,我们除了可以绘制三角形外还可绘制 点 和 线 。由于我们使用三角形,该参数应设为 D3DPT_TRIANGLELIST。
  • StartVetex 顶点数据流中标识顶点数据读取起点的元素的索引。该参数赋予我们一定的自由度,使我们可以只对顶点缓存中的某一部分进行绘制。
  • PrimitiveCount 所要绘制的图元数量。
    例如:
//画四个三角形
_device->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 4);

IDirect3DDevice9::DrawlndexedPrimitive

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);

BaseVertexlndex 参数的解释:
局部索引缓存的内容应与局部顶点缓存中的顶点一致。假定我们想将球,盒以及圆柱体的顶点合并到同一个全局缓存中。对于每个物体,我们必须重新计算索引,以保证这些索引正确的指向全局顶点缓存中对应的顶点。新索引的计算方法是为每个索引增加一个指定拉物体顶点在全局缓存中存储的起始位置的偏移量。注意,该偏移量用顶点的个数来度量而非字节。

Begin/End Scene

最后还有一点要指出的是,所有的绘制方法都必须在 IDirect3DDevice9::BeginScene 和 IDirect3DDevice9::EndScene 构成的方法之间进行调用。例如:

_device->BeginScene();
	_device->DrawPrimitive(...);
_device->EndScene();

总结:

  • 顶点数据存放在 VertexBuffer9 中,索引数据存放在 IndexBuffer9 中。使用顶点缓存或索引缓存的原因是数据是存放在显卡的内存上的。
  • 静态几何体数据应保存在静态顶点缓存或静态索引缓存中,而动态的几何体数据应保存在动态的顶点缓存或索引缓存中。所谓静态几何体就是在游戏中不需要改变的物体,比如一个房子,一座山;动态几何体在游戏中有动作,比如人物,怪物。他们的区别在于是否需要经常更新数据。
  • 绘制状态是图形设备维护的一个状态,该状态影响几何体的绘制方法。绘制状态从设定时起,直到下次修改之前都是有效的。
  • 为了绘制顶点缓存和索引缓存中的内容,需要先调用 IDirect3DDevice9::SetStreamSource 方法,并建立所要使用的顶点缓存与数据流的链接;如果使用了索引缓存,还必须调用 IDirect3DDevice9::SetIndices 方法对索引缓存进行链接;再调用 IDirect3DDevice9::SetFVF 告诉设备使用的自定义顶点格式;接着就可以在函数 IDirect3DDevice9::BeginScene 和 IDirect3DDevice9::EndScene 之间进行绘制工作了,如果绘制没有索引缓存的物体使用 IDirect3DDevice9::DrawPrimitive 函数,如果绘制使用了索引缓存的物体则必须使用 IDirect3DDevice9::DrawIndexedPrimitive 函数进行绘制。
  • 借助 D3DXCreate* 函数,我们可以创建比较复杂的3D物体,如球体,圆柱体,茶壶等。

以上所有内容都是在书中看到的,仅用于学习记录作用,另可供有需要的人查看,如有侵权,请联系我。

你可能感兴趣的:(D3D9学习笔记之顶点缓存与索引缓存)