(Visual C++)游戏开发笔记之三——绘制图元
使用Direct3D绘制图元
我们接下来的部分将介绍使用Direct3D在屏幕上绘图的图元。计算机图形学中的图元是可以渲染到屏幕上的基本形状。将图元组合在一起可以构成不同的形状和物体,诸如人物模型、场景物体、墙、建筑物等。最常用的图元是三角形。在游戏中常使用三角形,这是因为这样可以优化现代图形硬件,有效处理这类图元。其他图元包括四方形、直线和多边形。多边形被认为是图形,它是由直线构成的封闭区域。
Direct3D中在屏幕上绘制图元的方法主要有两种。
第一种是使用指针将几何图形数据数组发送给某个Direct3D函数。通常很少使用这种方法,因为这种方法运行缓慢,而且效率很低。这种方法之所以运行缓慢,是因为在Direct3D内部要对数据做一些不必要的工作,这样才能将数据下传给流水线,流水线负责绘制每个对象的每一帧。第二种方法是在绘制几何图形时使用一种名为顶点缓存的结构。顶点缓存是一个Direct3D对象,它可以按照Direct3D格式存储几何图形数据。借助用户指针(第一种方法),每次在程序中调用渲染函数时,Direct3D都不得不创建一个临时顶点缓存。这很浪费时间,尤其是如果信息没有发生变化,而且从技术角度而言,只需要创建顶点缓存一次时。为了使用用户指针在屏幕上绘制图元或图元组,就要调用Direct3D设备对象中的DrawPrimitive()函数。
该函数原型如下所示:
[cpp]
HRESULT DrawPrimitiveUP(
D3DPRIMITIVETYPE PrimitiveType,
UINT PrimitiveCount,
CONST void* pVertexStreamZeroData,
UINT VertexStreamZeroStride
);
HRESULT DrawPrimitiveUP(
D3DPRIMITIVETYPE PrimitiveType,
UINT PrimitiveCount,
CONST void* pVertexStreamZeroData,
UINT VertexStreamZeroStride
);
DrawPrimitiveup()函数的参数包括要绘制的图元类型、图元数量、保存要绘制的数据数组、顶点流大小。第一个参数是图元类型,如果要绘制的图元是一些点,那么该参数值可以设为D3DPT_POINTLIST;如果要绘制的图元是一些线段,那么该参数值可以设为D3DPT_LINELIST(例如:点1和点2连接,点3和点4连接);如果要绘制的图元是连接的线段,那么该参数值可以设为D3DPT_LINESTRIP(例如:点1和点2相连,点2和点3相连);如果要绘制的图元是一些离散的三角形,那么该参数值可以设为D3DPT_TRIANGLELIST(例如:点1、点2和点3构成一个三角形,而点4、点5和点6构成另一个三角形);如果要绘制的图元是一些相连的三角形,那么该参数值可以设为D3DPT_TRIANGLESTRIP(例如:点1、点2和点3构成一个三角形,点2、点3和点4构成另一个三角形);也可以设为D3DPT_TRIANGLEFAN(例如:点1、点2和点3构成一个三角形,而点1、点3和点4构成另一个三角形)。当处理三角形时,所选的三角形类型取决于将数据发送给渲染API的方式。如果发送给Direct3D API一列三角形,而三角形中的每个点互不相同,那么D3DPT_TRIANGLELIST就是最佳选择。
其余参数非常简单明了。DrawPrimitiveUP()函数的第二个参数是发送给函数的图元总数。如果将要绘制10个三角形,那么应该设该参数为10,除非只想绘制某一部分图元。第三个参数是发送给该函数的图元数据数组。第四个参数和最后一个参数是每个顶点的大小(单位:字节)。如果每个顶点都只指定了x、y、z浮点坐标位置数据,那么该参数值应该是12个字节。这因为它包含三个值,每个值分别是4个字节。
Direct3D顶点缓存
第二种方法(也即我们使用的方法)是使用Direct3D顶点缓存来存储和渲染几何图形数据。顶点缓存是LPDIRECT3DVERTEXBUFFER9类型的结构。该结构是LDirect3DVertexBuffer9的指针。在创建顶点指针时,要将其和正确渲染对象所需的全部信息一起加载。顶点缓存并不包含顶点数据。它同样可以保存顶点颜色、方向以及显示对象所需的其他属性。
调用Direct3D对象设备的CreateVertexBuffer()函数可以创建顶点缓存。只有成功创建Direct3D设备对象后,才可以调用该函数。
下面我们贴出CreateVertexBuffer()的函数原型。
[cpp]
HRESULT CreateVertexBuffer(UINT Length,
DWORD Usage,DWORD FVF, D3DPOOL Pool,
IDirect3DVertexBuffer9** ppVertexBuffer,
HANDLE* pSharedHandle);
HRESULT CreateVertexBuffer(UINT Length,
DWORD Usage,DWORD FVF, D3DPOOL Pool,
IDirect3DVertexBuffer9** ppVertexBuffer,
HANDLE* pSharedHandle);
CreateVertexBuffer()函数的第一个参数是顶点缓存所需的字节数。该参数值是正确存储一组几何图形(例如:人物模型、级别等)所需的最大数量(单位:字节)。第二个参数是一个描述顶点缓存使用方法的标识符,该值可设为0。第三个参数是一个描述使用顶点缓存的顶点结构的标识符或标识符集。例如,Direct3D需要知道数据的顺序和数据类型(先是顶点位置、接下来是顶点颜色等),这样才可以正确地绘制几何图形。第四个参数是放置资源的正确内存类。第五个参数是顶点缓存(LPDIRECT3DVERTEXBUFFER9类型),函数是否成功决定了是否可以创建该缓存。CreateVertexBuffer()函数中的第一个参数为保留参数,值总是为0。
创建顶点缓存后,接下来要做的工作是锁定顶点缓存。调用顶点缓存的Lock()函数就可以获取顶点缓存指针,从而将数据复制给该顶点缓存。Lock()函数将锁定一列数据,这样就可以处理该数据了。该函数的参数包括:为上锁的顶点缓存增加的偏移量(0代表锁定全部缓存)、要锁定的数据量、指向锁定内存区的指针以及一个指定锁存数据方法的标识符。将数据复制到缓存的最简单方法是使用标准函数memcpy()。一旦复制完数据,就可以调用Unlock()。每调用一次Lock()函数就必须相应地调用一次Unlock()函数。本章稍后将介绍如何创建和显示几何图形。
坐标系
默认情况下,Direct3D使用的是左手坐标系。这意味着虚拟3D网格上的每个正坐标远离观察者的方向。例如,Z轴的正坐标是冲向屏幕里面的,Y轴的正坐标是向上的,而X轴的正坐标是向右的。图1.3给出了该坐标系的示意图。
对OpenGL用户而言,一定要记住,OpenGL使用的是右手坐标系,而Direct3D使用的是左手坐标系。这意味着除了Y轴之外,所有轴的正方向都是相反的。
虽然也可将Direct3D设为右手坐标系,但默认情况下,它使用的是左手坐标系。下面将介绍许多和坐标系打交道的函数。每个函数都有另一个可供选择的使用右手坐标系的形式。这样就可以很轻松地从Direct3D移植到OpenGL。
我们来写一个Lines(线段)演示程序。该演示程序将在屏幕中间绘制两条白色线段。
该程序的main源文件的全局部分代码如下所示:
[cpp]
#include<d3d9.h>
#define WINDOW_CLASS "UGPDX"
#define WINDOW_NAME "Drawing Lines"
// Function Prototypes...
bool InitializeD3D(HWND hWnd, bool fullscreen);
bool InitializeObjects();
void RenderScene();
void Shutdown();
// Direct3D object and device.
LPDIRECT3D9 g_D3D = NULL;
LPDIRECT3DDEVICE9 g_D3DDevice = NULL;
// Vertex buffer to hold the geometry.
LPDIRECT3DVERTEXBUFFER9 g_VertexBuffer = NULL;
// A structure for our custom vertex type
struct stD3DVertex
{
float x, y, z;
unsigned long color;
};
// Our custom FVF, which describes our custom vertex structure.
#define D3DFVF_VERTEX (D3DFVF_XYZ | D3DFVF_DIFFUSE)
#include<d3d9.h>
#define WINDOW_CLASS "UGPDX"
#define WINDOW_NAME "Drawing Lines"
// Function Prototypes...
bool InitializeD3D(HWND hWnd, bool fullscreen);
bool InitializeObjects();
void RenderScene();
void Shutdown();
// Direct3D object and device.
LPDIRECT3D9 g_D3D = NULL;
LPDIRECT3DDEVICE9 g_D3DDevice = NULL;
// Vertex buffer to hold the geometry.
LPDIRECT3DVERTEXBUFFER9 g_VertexBuffer = NULL;
// A structure for our custom vertex type
struct stD3DVertex
{
float x, y, z;
unsigned long color;
};
// Our custom FVF, which describes our custom vertex structure.
#define D3DFVF_VERTEX (D3DFVF_XYZ | D3DFVF_DIFFUSE)
上段代码的开始部分包含了Direct3D头文件d3d9.h。该文件是必需的,只有包含该文件才能使用Direct3D函数和结构。代码中接下来的两行是全局定义语句,这两个语句定义了窗口类名称和窗口标题。WinMain()函数将用到这两个字符串,并且为方便起见,可以在全局区对它们进行处理。
定义语句后面的几行代码是代码中将要用到的不同函数的原型定义。InitializeD3D()函数用于在程序中设置和创建Direct3D。InitializeObjects()函数用于创建演示程序中要绘制在屏幕上的物体。RenderScene()函数用于在屏幕上渲染图形,而Shutdown()函数用于在程序退出时清除程序。
函数原型后面声明了一个顶点缓
补充:软件开发 , Vc ,