1.D3D 从0到一个图元的绘制 的步骤
(1).初始化D3D(LPDIRECT3D9)和Device(LPDIRECT3DDEVICE9)对象。
这里的主要作用是:设置back buffer的个数、格式,还有在swapBuffer之后对buffer的处理方法,比如back buffer的内容被删除或者back buffer的内容部变;是否启用 z-buffer 或者 模板缓存 和 这两个缓存的格式; 全屏渲染和抗锯齿效果等。
(2).准备顶点数据:让VB(vertex buffer)和Device关联起来,并把已知的一组数据写入到VB里面
(3).清除back buffer的当前缓冲器(也可以有深度缓冲器、模板缓冲),设置世界矩阵、投影矩阵和视点
(4).设置device的当前VB,并且设置这个VB的格式,然后绘制图元
(5).程序结束,释放资源。
其中(1)、(3)、(5)的内容可以在 <<从0到第一个D3D程序>> 里面找到
2.对第(2)步准备数据的说明
(1)顶点缓存的概念和创建的方法
顶点缓存是一片连续的内存空间,是由D3D分配和管理的,是用来存储顶点数据的。
顶点数据的内容可以包含顶点的(x,y,z)坐标数据、顶点的颜色数据(diffuse)、纹理坐标数据等等。
顶点缓冲和D3D对象一样,也是一个对象: LPDIRECT3DVERTEXBUFFER9, 它也是由D3D进行管理的。
在device里面给一个 VB 对象分配内存和设置这个VB对象属性的方法是:
HRESULT IDirect3DDevice9::CreateVertexBuffer(
UINT Length,
DWORD Usage,
DWORD FVF,
D3DPOOL Pool,
IDirect3DVertexBuffer9** ppVertexBuffer,
HANDLE* pSharedHandle
);
Length —— 分配给缓存的字节大小。假如想得到一个能存储8个顶点的顶点缓存,那么我们就要在顶点结构中设置这个参数为 8 * sizeof ( Vertex ) 。
Usage —— 指定关于怎样使用缓存的额外信息。这个值可以是0,没有标记,或者是下面标记的一个或多个的组合:
D3DUSAGE_DYNAMIC——设置这个参数可以使缓存是动态的。
D3DUSAGE_POINTS——这个参数指定缓存存储原始点。这个参数仅仅用在顶点缓冲中。
D3DUSAGE_SOFTWAREPROCESSING——使用软件顶点处理
D3DUSAGE_WRITEONLY——指定应用程序只能写缓存。它允许驱动程序分配最适合的内存地址作为写缓存。注意如果从创建好的这种缓存中读数据,将会返回错误信息。
FVF —— 存储在缓存中的顶点格式。
顶点缓冲的数据格式及其值的type的对应关系见:http://www.cnblogs.com/xmzyl/articles/1604096.html
Pool —— 缓存放置在哪一个内存池中.我们采用的是D3DPOOL_DEFAULT,意思是顶点缓冲区尽可能存在显存中。
ppVertexBuffer ——返回创建好的顶点缓存的指针。
pSharedHandle ——没有使用;设置为0。
注意:不使用D3DUSAGE_DYNAMIC参数创建的缓存被叫做静态缓存。静态缓存通常被放置在显存中,在其中的数据能被很有效的处理。然而,对于静态缓存,从中读取和写入数据是很慢的,因为访问显存是很慢的。因为这个原因我们用静态缓存存储静态数据(不需要被经常改变的数据)。地形和建筑物是很好的候选例子,因为在应用程序中他们通常不需要被改变。静态缓存应该在应用程序初始化的时候就被填充好,而不是在运行时才做。
注意:使用D3DUSAGE_DYNAMIC参数创建的缓存被叫做动态缓存。动态缓存通常被放在AGP内存中,这种内存中的数据能被很快的更新。处理动态缓存中的数据不会比处理静态缓存中的数据快,因为这些数据必须在渲染前被转移到显存中,动态缓存的好处是它们能够被稍微快点地被更新(比CPU写快)。因此,假如你需要经常更新缓存中的数据,那么你就应该使用动态缓存。粒子系统是很好的一个应用,因为它们是动态的,并且他们通常每一帧都会被更新。
注意:在程序中读取显存和AGP内存都是非常慢的。因此,假如你在运行时需要读取你的几何物体,最好的方案是指定一块系统内存,往其中拷贝并且读取数据。
(2)顶点缓存的读和写
我们通过得到一个顶点缓存的内存指针来读写缓存中的数据,方法是lock:
HRESULT IDirect3DVertexBuffer9::Lock(
UINT OffsetToLock,
UINT SizeToLock,
BYTE** ppbData,
DWORD Flags
);
OffsetToLock —— 偏移量,以字节为单位,从缓存开始位置到锁定开始位置的距离。
SizeToLock —— 锁定的字节数。
ppbData —— 一个指向锁定内存开始位置的指针。
Flags —— 标记描述怎样锁定内存。它可能是0或者是下面参数中的1个或多个的组合:
D3DLOCK_DISCARD——这个参数仅仅会在动态缓存时被使用。它指示硬件丢弃缓存并返回一个指向新分配的缓存的指针。这是很有用,因为当我们存取一个新分配的缓存时它允许硬件继续从丢弃的缓存渲染。这防止了硬件延迟。
D3DLOCK_NOOVERWRITE——这个参数仅仅会在动态缓存时被使用。它声明你将向缓存中添加数据。即你不能向已经渲染的内存中写数据。这是有好处的因为他允许你在添加新数据到缓存的同时让硬件继续渲染。
D3DLOCK_READONLY——这个参数声明你锁定的缓存只能从中读取数据而不能写数据。这允许一些内在的优化。
用参数D3DLOCK_DISCARD和D3DLOCK_NOOVERWRITE表明缓存的一部分被锁定之后能继续被使用。假如硬件配置允许这些标记被使用,则在对缓存进行锁定时,其他的显示操作就不会中断。
下边的例子展示了通常怎样使用Lock方法。注意当我们使用完以后要调用Unlock方法。
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(); // 当你访问完缓存时,解锁缓存
3.对第(4)步的说明
设置device的当前VB,并且设置这个VB的格式,然后绘制图元。
步骤:
m_pDevice->SetStreamSource(0,m_pVB,0,sizeof CUSTOMVERTEX);
m_pDevice->SetFVF(D3DFVF_CUSTOMVERTEX);
m_pDevice->DrawPrimitive(D3DPT_LINELIST,0,3);//这里的图元类型是 D3DPT_LINELIST,是一些线段。
//图元相关可以参见:http://blog.csdn.net/niyongfu14/article/details/6617977
HRESULT SetStreamSource(
UINT StreamNumber, //渲染数据流序号,数据流的概念会在纹理相关处理时遇到,现在就为0
IDirect3DVertexBuffer9 *pStreamData, //进行绑定的顶点缓冲区指针
UINT OffsetInBytes, //进行绑定连接的渲染数据流的起始位置
UINT Stride //渲染数据流中一个顶点所占的内存的大小
);