6. 绘制 3D基本形状
现在您已学到了如何以Microsoft Direct3D立即模式来建立3D环境,整个迷宫只剩下最后一个关键片段:建立一个3D对象并绘制出来。在第五章中,您看到了Direct3D中的所有可用顶点。您也看到了如何自行为顶点配置内存或自行使用顶点缓冲区去管理内存。在本章中,您可以用这些顶点来建立3D基本形状(3D primitive)。
所谓3D基本形状是指由一群顶点组成的对象。您所能建立的最基本形状是一个点串行(point list),它只是一些单一像素点的集合。您也可以建立并绘制一个线串行(line list),这是一个单像素宽线条的集合。然而您所建立的通常是三角形。Direct3D使用三角形(而非其它种多边形)来描述3D对象的各个面,因为三角形是共平面且是凸面形状(意谓他们比较容易快速绘制)。Direct3D让您可以结合三角形来建立更复杂的多边形和网格。如果运用大量的三角形时,您还可以达到逼近弧面(如球体)的效果。
BeginScene和EndScene方法
在进入绘制基本形状的细节之前,您应该要了解BeginScene和EndScene。在Direct3D中,您可以用IDirect3DDevice7::BeginScene方法来表示要开始绘制某个场景。这个方法会告诉DirectX检查绘制数据和确定成像绘图页已经设定好了。
一旦使用该方法后,您可以开始用Direct3D方法来绘制场景中的对象所需的基本形状。如果您在呼叫BeginScene前呼叫这些方法,Direct3D会传回
D3DERR_SCENE_NOT_IN_SCENE。一旦您的绘制工作告一段落。您必须去呼叫IDirect3DDevice7::EndScene来清除内部旗标(表示场景绘制进行中),更新快取数据,确定成像绘图页已完成。
您必须以BeginScene/EndScene组合把所有的对绘制方法的呼叫包起来。如果
BeginScene失败了,场景就不会开始,而且呼叫Endscene也会失败(因为一开始就没有启动场景)。如果在呼叫BeginScene前某个绘图页没有回复,则它会传回
DDERR_SURFACELOST。如果绘图页在绘制过程中不见了, EndScene也会传回此错误值,而在呼叫绘制方法时也回传回错误。注意一旦BeginScene成功之后,如果在场景绘制过程中传回了错误,您必须去呼叫EndScene来完成您的绘制过程。
记住一件重要的事,您必须先结束一个场景的绘制作业,才能再开始另外一个。如果您试着要使用巢状BeginScene/EndScene组合,就会传回D3DERR_SCENE_IN_SCENE错误。如果您呼叫了EndScene但没有呼叫BeginScene,也会有这种错误。
索引和非索引的基本形状
立即模式提供了二种将基本形状顶点分类的方式:使用非索引基本形状和使用索引基本形状。要建立一个非索引基本形状,您可以用有顺序的顶点序列填入数组中。这里的顺序是指由数组中顶点顺序指示如何产生三角形。第一个三角形是由数组中的前三个顶点所组成,第二个三角形是由接下来三个点所组成,依此类推。这意谓着多个三角形不能共享顶点。如果您有二个相连的三角形,您必须要定义同一个顶点很多次。要建立一个索引基本形状,你可以用没有顺序的顶点序列填入数组中。并且建立第二个数组,并且用一个有顺序的索引序列指向这个无顺序的数组中。对索引基本形状而言,数组中的顶点顺序并不重要。而是由索引数组中的索引顺序指示如何造出三角形。第一个三角形是由索引数组中的前三个索引所参照的顶点所组成,第二个三角形是由接下来三个索引所参照的顶点所组成,依此类推。这意谓着多个三角形可以共享顶点,只要让索引数组中的多个项目参照到同一个顶点即可。本章稍后会谈到的三角条(strip)和扇,也是可以共享顶点,所以您应该依据3D对象的结构来选择使用索引或非索引基本形状。
大部分的3D模型分享多个顶点。因此,您可以让多个三角形间(或线条)分享这些顶点来节省内存、频宽以及CPU时间。当3D模型分享顶点时,索引顶点是很有用的。然而要小心某些看起来可以共享,事实却不然的顶点。立方体就是个典型的例子。让构成立方体的12个三角形只须共享8个顶点,看起来很诱人。但是如果您这样做,就会在这些点的法向量出问题。每个三角形的面都有一个向量,称为表面法向量(face normal),和平面垂直。这个表面法向量指向远离三角形面的前方边缘,在Direct3D中这是唯一可见边。因为法向量是依顶点而非面来定义,二个不同法向量的三角形就不能共享顶点。即使他们可以,但是被共享的顶点只能有一个法向量,所以就无法产生您想得到的打光效果。在立方体的情况中,事实上使用24个顶点会比较好,甚至比12个三角形所必须的36个顶点(不用索引时)还要好。图6-1说明了这些法向量的组成。
图6-1 一个立方体表面的顶点法向量
DrawPrimitive方法
您可能开始觉得在绘制基本形状时有许多不同的情况要考虑。您已经学到顶点有三种储存方式:使用者数组、间插顶点和顶点缓冲区。而且您已学到Direct3D可以绘制三种基本形状:点、线和三角形。线可以分成二种:线串行(line list)和线条块。三角形可以区分成三种:串行、条块和扇。最后,基本形状可以是索引或非索引的。如此一来,又增加了绘制基本形状时的许多不同方式!IDirect3DDevice7接口的6种DrawPrimitive方法可以处理所有的情况。表6-1说明了针对各种顶点型态和索引型态组合所适用的不同方法。
表6-1绘制基本形状的方法
基本形状的型态 一般的使用者顶点 非索引 索引 DrawPrimitive 间插的使用者顶点 DrawPrimitiveStrided 顶点缓冲区 DrawPrimitiveVB DrawIndexedPrimitive DrawIndexedPrimitiveStrided DrawIndexedPrimitiveVB 让我们再详细看看这些方法
DrawPrimitive
IDirect3DDevice7::DrawPrimitive方法会依您所提供作为任意几何基本形状类型的顶点数组来绘制。以下是本方法的函式宣告:
HRESULT IDirect3DDevice7::DrawPrimitive( ); 参数 dptPrimitiveType 说明 这个指令所要绘制的基本形状型态。这个基本形状型态必须是 D3DPRIMITIVE列举型态的成员之一。 储存弹性顶点格式旗标的组合,用来定义这些基本形状集合会用到的顶点格式。 指到在基本形状序列中用到的顶点数组的指针。 D3DPRIMITIVETYPE dptPrimitiveType, DWORD dwVertexTypeDesc, LPVOID lpvVertices, DWORD dwVertexCount, DWORD dwFlags dwVertexTypeDesc lpvVertices dwVertexCount 储存了数组中画出的顶点数目。您可以指定的最大值是 D3DMAXNUMVERTICES(65,535)。 将本参数设为0可在绘制基本形状时不用等待,如果设为 D3DDP_WAIT,则使得方法会在回传前保持等待,直到多边形 完成绘dwFlags 制。预设情形下,这个方法会在将多边形传送到视讯卡后立刻回传。 可利用D3DDP_WAIT旗标值来除错。程序不应在绘制前使用 D3DDP_WAIT旗标来等待场景更新。 DrawPrimitiveStrided
IDirect3DDevice7::DrawPrimitiveStrided方法是用间插顶点数组绘出多个几何形状。您必须检查想绘制的顶点是否使用了所指定的格式。因为Direct3D不会检查顶点大小和间插是否符合已设定的弹性顶点格式,您必须自行确认,否则就可能发生内存错误。 这里是方法的函式宣告:
HRESULT DrawPrimitiveStrided( ); 参数 dptPrimitiveType 说明 这个指令要绘制的基本形状型态。这个参数必须是 D3DPRIMITIVETYPE列举型态成员之一。 D3DPRIMITIVETYPE dptPrimitiveType, DWORD dwVertexTypeDesc, LPD3DDRAWPRIMITIVESTRIDEDDATA lpVertexArray, DWORD dwVertexCount, DWORD dwFlags dwVertexTypeDesc 描述顶点格式的弹性顶点格式旗标的组合。 D3DDRAWPRIMITIVESTRIDEDDATA结构的地址,这个结构中 包含了基lpVertexArray 本形状的顶点组件指针和内存间插值,这个参数的格 式是由dwVertexTypeDesc中的旗标所设定。 dwVertexCount lpVertexArray数组中的顶点数目。可容许的最大顶点数是D3DMAXNUMVERTICES(0xFFFF)。 设为0可在绘制基本形状时不用等待,如果使用D3DDP_WAIT旗标,则使得方法会在回传前保持等待,直到多边形完成绘制, 而不会在多边形dwFlags 传送到视讯卡后立刻回传。(对场景撷取卡而言,当卡响应时方法会立刻回传。) 本旗标值通常是来除错。程序不应用D3DDP_WAIT旗标来等待 场景更新后再继续。 DrawPrimitiveVB
IDirect3DDevice7::DrawPrimitiveVB方法是用顶点缓冲区的顶点数组绘出多个几何基本形状。这里是方法的函式宣告:
HRESULT DrawPrimitiveVB( ); 参数 pptPrimitiveType 说明 这个指令要绘制的基本形状型态。这个参数必须是 D3DPRIMITIVETYPE列举型态成员之一。 包含了顶点数组的顶点缓冲区的IDirect3DVertexBuffer7接口地址。顶点可以是已转换或未转换,最佳化或未最佳化。 在基本形状中的第一个顶点的索引值。最大可能启始索引是dwStartVertex D3DMAXNUMVERTICES(0xFFFF)。在除错过程中,如果指定 一个超过此极值的启始索引值时,会造成方法失败且传回 DDERR_INVALIDPARAMS。 dwNumVertices 绘制的顶点数。可容许的最大顶点数是D3DMAXNUMVERTICES (0xFFFF)。 设为0可在绘制基本形状时不用等待,如果使用D3DDP_WAIT 旗标,则使得方法会在回传前保持等待,直到多边形完成绘制, 而不会在多边形传送dwFlags 到视讯卡后立刻回传。(对场景撷取卡而 言,当卡响应时方法会立刻回传。) 本旗标值通常是来除错。程序不应用D3DDP_WAIT旗标来等待 场景更新D3DPRIMITIVETYPE d3dptPrimitiveType , LPDIRECT3DVERTEXBUFFER7 lpd3dVertexBuffer, DWORD dwStartVertex , DWORD dwNumVertices , DWORD dwFlags lpd3dVertexBuffer