基本图形

图元

游戏中的模型不管复杂度多高,都是由点、线、三角形面拼接而成的。

图元(Primitive)是由Direct3D定义的基本的图形表示单位,而所有的复杂物体都是由这些基本图元来组合而成的。

图元类型 Direct3D定义 含义
点列集合 D3DPT_POINTLIST 一组点的集合
线列集合 D3DPT_LINELIST 一组线段的集合
线带集合 D3DPT_LINESTRIP 首尾相连的线段的集合
三角形列 D3DPT_TRIANGLELIST 一组三角形的集合
三角形带 D3DPT_TRIANGLESTRIP 首尾相连的三角形,有两个顶点重合
三角形扇 D3DPT_TRIANGLEFAN 组成扇形的一组三角形

DrawPrimitive是Direct3D提供的基本图元的绘制函数之一

HRESULT DrawPrimitive(

    D3DPRIMITIVETYPE PrimitiveType,//基本图元类型
    const void* pVertexStreamZeroData,//顶点的起始地址
     UINT PrimitiveCount);//绘制的图元的数量

图形绘制

点列(POINTLIST):点列由一系列的顶点组成。

如:

g_pDevice->DrawPrimitive(D3DPT_LINELIST,数组首地址,绘制个数);

线列(LINELIST):线列由一系列的线段组成。

如:

g-pDevice->DrawPrimitive(D3DPT_LINRLIST,数组首地址,绘制的个数);

线带(LINELIST):线带由一系列的线段组成,前一个线段的终点是下一个线段的起点。

g_pDevice->DrawPrimitive(D3DPT_LINELIST,数组首地址,绘制个数);

三角形列(TRIANGLELIST):三角形列由一系列三角形组成。

如:

g_pDevice->DrawPrimitive(D3DPT_TRIANGLELIST,数组首地址,绘制个数);

三角形带(TRIGANLESTRIP):三角形带是由一系列的三角形组成,除了第一个三角形,其他三角形只需要输入第三个顶点,这个顶点与前一个三角形中的后两个顶点组成新的三角形。

三角形扇(TRIANGLEFAN):三角形是以扇形扩展的方式来定义三角形序列的。

如:

g_pDevice->DrawPrimitive(D3DPT_TRIANGLEFAN,数组首地址,绘制个数);

顶点的格式

顶点(Vertex)是一切物体的最基本元素,对应着三维空间坐标系中的一个点。

在Direct3D中,描述一个点的方法非常丰富灵活,可以通过特殊的组合来描述空间中点的各种属性。Direct3D引入了一个称为灵活顶点格式(FVF:Flexible Vertex Format)的概念。

灵活顶点格式是用来描述顶点属性的一种方法,这种方法可以由自己来定义顶点格式。FVF这种灵活的顶点组织方式使图形绘制中只需要使用顶点必要的组成部分,从而节省了大量的内存宽带和渲染时间。

说明
D3DFVF_DIFFUSE 顶点漫反射颜色值
D3DFVF_NORMAL 顶点格式包含法向量
D3DFVF_PSIZE 指明绘制顶点的大小
D3DFVF_SPECULAR 顶点镜面反射颜色值
D3DFVF_XYZ 顶点是未经坐标转换的的坐标
D3DFVF_XYZRHW 顶点包括经过坐标转换的坐标

D3DFVF_XYZB1

...

D3DFVF_XYZB5

顶点格式包括用于骨骼动画的定点和顶点对骨骼的权值信息
D3DFVF_XYZW 顶点格式包括经过坐标转换和裁剪的顶点坐标

D3DFVF_TEX0

...

D3DFVF_TEX8

顶点格式包括0~8个纹理坐标

注意:如果使用灵活顶点格式,必须以以下顺序来格式化所有的顶点:位置,RHW,混合加权值,反射颜色,纹理坐标集(1-8套)。

顶点的定义

  1. 根据自己的需要定义点的格式
  2. 定义完点的格式后,必须 声明该点的格式
  3. 定义顶点

例如:

1.声明一个包含经过转换的坐标和点的颜色

Struct CUSTOMVERTEX
{
    FLOAT x,y,z,rhw;//经过坐标转换的顶点格式
    DWORD color;//顶点漫反射颜色值
}
#define D3DFVF_CUSTOMVERTEX(D3DFVF_XYZRHW|D3DFVF_DIFFUSE)

2.声明一个顶点的具体坐标,点的法向量坐标,纹理坐标

Struct CUSTOMVERTEX
{
    FLOAT x,y,z;
    FLOAT nx,ny,nz;
    FLOAT tu,tv;
}
#define D3DFVF_CUSTOMVERTEX(D3DFVF_XYZ|D3DFVF_NORMAL|D3DFVF_TEX1)

屏幕坐标系定义顶点

//声明顶点的位置和漫反射数据
CUSTOMVERTEX g_VerTices[]=
{
    // x      y     z   rhw   漫反射颜色
    {100.0f,100.0f,0.0f,1.0f,0xffff0000},
    {200.0f,100.0f,0.0f,1.0f,0xff00ff00},
    {100.0f,200.0f,0.0f,1.0f,0xff0000ff},
    {200.0f,200.0f,0.0f,1.0f,0xffffffff},
};

注意:

  1. 二维图形的绘制,如果以屏幕坐标系定义,顶点的格式必须为D3DFVF_XYZRHW
  2. 若图元为三角形,必须保证顶点的定义顺序为顺时针。DX默认仅渲染顺时针三角形
  3. 若使用的三角形strip图元,仅需保证第一个三角形为顺时针即可

相对坐标系定义定点

//声明顶点的位置和漫反射数据
CUSTOMVERTEX g_VerTices[]=
{
    // x      y     z   rhw   漫反射颜色
    {-0.5f,0.5f,0.0f,1.0f,0xffff0000},
    {0.5f,0.5f,0.0f,1.0f,0xff000000},
    {-0.5f,-0.5f,0.0f,1.0f,0xff0000ff},
    {0.5f,-0.5f,0.0f,1.0f,0xffffffff},
};

注意:

  1. 相对坐标系,以窗口的中心为原点,X轴往右为正,Y轴往上为正,z轴往里为正
  2. 不管窗口宽高,x轴范围为-1~1,y轴范围为-1~1,z轴范围为0~1
  3. 二维图形的绘制,如果以相对坐标系定义,定义的格式必须为D3DFVF_XYZ

图形渲染工作一般都是在函数BeginScene和EndScene之间完成。绘制函数之前一定要通知显卡所绘制图形的顶点格式。

如:

g_pDevice->SetFVF(D3DFVF_CUSTOMVERTEX);
g_pDevice->DrawPrimitive(D3DPT_TRIANGLEFAN,数组首地址,绘制个数);

顶点缓冲区

顶点缓冲区(Vertex Buffer)是Direct3D用来保存顶点数据的内存缓冲区。顶点缓冲区可以设置在显卡的显存中、AGP内存中或者是在系统内存中。如果指定缓冲区空间分配在显存中,能够大大的提高渲染的效率。

使用函数CreateVertexBuffer创建一个顶点缓冲区用来保存矩阵顶点值。

HRESULT CreatetexBuffer(
    UINT Length,//按缓冲区大小,按字节数算
    DWORD Usage,//顶点缓冲区属性
    DWORD FVF,//灵活顶点格式
    D3DPOOL Pool,//顶点缓冲区内存类型
    IDirect3DVertexBuffer9** ppVertexBuffer,//顶点缓冲区指针地址
    HANDLE* pHandle//保留参数,置为0
};



LPDIRECT3DVERTEXBUFFER9 g_pVB = NULL;//要创建的顶点缓冲
//其中参数D3DFVF_CUSTOMVERTEX指定的自定义格式
if(FAILED(g_pd3dDevice->CreateVertexBuffer(sizeof(Rectangle),0,
D3DFVF_CUSTOMVERTEX,D3DPOOL_DEFAULT,&g_pVB,NULL)))
{
    return E_FAIL;
}

其中,参数Usage用于指定顶点缓冲区的属性,其取值可以设为0或下表中的任意值的组合。

缓冲区属性 说明
D3DUSAGE_DONOTCLIP

禁用裁剪,表示顶点缓冲区中的顶点不进行裁剪,当设置该属性时,渲染状态D3DRS_CLIPPING必须设为FALSE

D3DUSAGE_DYNAMIC 使用动态内存
D3DUSAGE_NPATCHES 使用顶点缓冲区绘制N-patches曲线
D3DUSAGE_POINTS 使用顶点缓冲区绘制点
D3DUSAGE_RTPATCHES 使用顶点缓冲区绘制曲线
D3DUSAGE_SOFTWAREPROCESSING 使用软件进行顶点运算,否则使用硬件计算
D3DUSAGE_WRITEONLY 只写属性,不能进行读操作,设置改属性可以提高系统性能

参数Pool属于枚举类型D3DPOOL,指定顶点缓冲区资源的内存位置。

枚举类型POOL值 说明
D3DPOOL_DEFAULT 默认值,顶点缓冲区尽可能存在于显存中
D3DPOOL_MANAGED 由Direct3D自动调度顶点缓冲区内存位置(显存或内存)
D3DPOOL_SYSTEMMEN 顶点缓冲区位于内存中
D3DPOOL_SCRATCH 顶点缓冲区位于计算机的临时内存中,这种类型的顶点缓冲区不能直接进行渲染,只能进行内存加锁、拷贝等操作。
D3DPOOL_FORCE_DWORD 强制编译为32位,不使用

保存顶点到顶点缓冲区

需要把顶点的值存入顶点缓冲区之中,常用的方法是使用函数Lock()来获取顶点缓冲区的首地址,并且锁定该缓冲区仅供当前操作使用。

HRESULT IDiret3DVertexBuffer9::Lock(
    UINT OffsetToLock,//加锁内存起始地址
    UINT SizeToLock,//加锁内存大小
    VOID** ppbData,//返回内存地址
    DWORD Flags//加锁属性
);

Lock()函数执行结束时,必须使用UnLock()函数对缓冲区解锁。

VOID** pRectangle;
if(FAILED(g_pVB->Lock(0,sizeof(Rectangle),(void**)&pRectangle,0)))
    return E_FAIL;
memcpy(pRectangle,Rectangle,sizeof(Rectangle));
g_pVB->UnLock();

函数memcpy的功能是将创建好的顶点内容复制到顶点缓冲区中。

从顶点缓冲区绘制图形

设置全局的渲染状态

	//关闭光照                    状态类型        值
	g_pd3dDevice->SetRenderState(D3DRS_LIGHTING, false);

	//关闭背面拣选
	g_pd3dDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);//即前背双面渲染

设置资源流

Direct3D中提供了函数SetStreamSource将设置好的顶点缓冲区对象绑定到数据流上:

HRESULT SetStreamSource(
    UINT StreamNumber,//渲染数据流信号
    IDirect3DVertexBuffer9** pStreamData,//进行绑定连接的顶点缓冲区指针
    UINT OffsetInVutes,//进行绑定连接的渲染数据流的起始位置
    UINT Stride//渲染数据流中一个顶点所占的内存的大小
);

设置顶点灵活格式

//设置顶点的灵活格式
g_pd3dDevice->SetFVF(CUSTOM_VERTEX_FVF);

绘制基本图元

Direct3D中使用函数IDirect3DDevice9::DrawPrimitive()完成绘制图元。

HRESULT DrawPrimitive(
    D3DPRIMITIVETYPE PrimitiveType,//基本图元类型
    UINT StartVertex,//起始顶点,即绘制图元时,使用到顶点缓冲区中的起始地址
    UINT PrimitiveCount//绘制的图元的数量
);

完整代码示例:

//-----------------------------------------------------------------------------
// File: main.cpp
//-----------------------------------------------------------------------------
#include 
#include 
#pragma warning( disable : 4996 ) //屏蔽一些警告
#include 
#pragma warning( default : 4996 )

#pragma comment(lib,"d3d9.lib")//引入dx的静态库

//顶点数据结构体
struct CustomVertex
{
	FLOAT x, y, z, w;//经过变换的顶点坐标
	DWORD color;//顶点的颜色
};

//声明顶点灵活格式
#define CUSTOM_VERTEX_FVF (D3DFVF_XYZRHW | D3DFVF_DIFFUSE)

//-----------------------------------------------------------------------------
// Global variables
//-----------------------------------------------------------------------------
LPDIRECT3D9         g_pD3D = NULL; // 引擎指针,用来创建设备指针
LPDIRECT3DDEVICE9   g_pd3dDevice = NULL; // 设备指针,所有渲染相关的操作全部是用设备指针来完成
LPDIRECT3DVERTEXBUFFER9 g_pVB = NULL; //顶点缓冲区

//初始化顶点数据
HRESULT InitVertex()
{
	//定义顶点数组
	CustomVertex vertexs[] =
	{
		{ 100, 0, 1, 1, D3DCOLOR_XRGB(255, 0, 0) },
		{ 0, 0, 1, 1, D3DCOLOR_XRGB(0, 255, 0) },
		{ 0, 100, 1, 1, D3DCOLOR_XRGB(0, 0, 255) },
		{ 100, 200, 1, 1, D3DCOLOR_XRGB(255, 255, 0) },
		{ 300, 200, 1, 1, D3DCOLOR_XRGB(55, 205, 23) },
	};

	//创建顶点缓冲区
	if (FAILED(g_pd3dDevice->CreateVertexBuffer(sizeof(vertexs), //顶点缓冲区的字节大小
		0, //缓冲区属性,一般填0
		CUSTOM_VERTEX_FVF, //顶点的灵活格式
		D3DPOOL_DEFAULT, //内存类型,一般填D3DPOOL_DEFAULT(默认的)
		&g_pVB,//顶点缓冲区的地址
		0)))
	{
		return E_FAIL;
	}

	CustomVertex* pVertex;
	//填充顶点缓冲区
	//加锁(1.安全处理,2返回顶点缓冲区的首地址)
	if (FAILED(g_pVB->Lock(0,//加锁地址的偏移量
		sizeof(vertexs), //加锁内存的大小
		(VOID**)&pVertex,//加锁内存的首地址
		0)))
	{
		return E_FAIL;
	}

	//复制数据
	memcpy(pVertex, vertexs, sizeof(vertexs));

	//解锁
	if (FAILED(g_pVB->Unlock()))
	{
		return E_FAIL;
	}

	return S_OK;
}


//-----------------------------------------------------------------------------
// Name: InitD3D()
// Desc: 初始化D3D
//-----------------------------------------------------------------------------
HRESULT InitD3D(HWND hWnd)
{
	// 创建D3D引擎指针
	if (NULL == (g_pD3D = Direct3DCreate9(D3D_SDK_VERSION)))
		return E_FAIL;

	//设备参数结构体
	D3DPRESENT_PARAMETERS d3dpp;
	ZeroMemory(&d3dpp, sizeof(d3dpp));//让一段内存归零
	d3dpp.Windowed = TRUE;//是否窗口化
	d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;//交换方式
	d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;//后台缓冲区的格式

	//创建设备指针
	if (FAILED(g_pD3D->CreateDevice(
		D3DADAPTER_DEFAULT, //显示设备的编号
		D3DDEVTYPE_HAL,//设备类型
		hWnd,//窗口句柄
		D3DCREATE_SOFTWARE_VERTEXPROCESSING,//顶点处理方式
		&d3dpp,//设备参数结构体
		&g_pd3dDevice)))//设备指针的地址
	{
		return E_FAIL;
	}

	//初始化顶点数据
	if (FAILED(InitVertex()))
	{
		return E_FAIL;
	}

	//关闭光照
	g_pd3dDevice->SetRenderState(D3DRS_LIGHTING, false);

	//关闭背面拣选
	g_pd3dDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);

	return S_OK;
}




//-----------------------------------------------------------------------------
// Name: Cleanup()
// Desc: Releases all previously initialized objects
//-----------------------------------------------------------------------------
VOID Cleanup()
{
	if (g_pd3dDevice != NULL)
		g_pd3dDevice->Release();

	if (g_pD3D != NULL)
		g_pD3D->Release();
}




//-----------------------------------------------------------------------------
// Name: Render()
// Desc: Draws the scene
//-----------------------------------------------------------------------------
VOID Render()
{
	if (NULL == g_pd3dDevice)
		return;

	//1.清空后台缓冲(其中包括颜色,深度,模板)
	g_pd3dDevice->Clear(0, //要清空的矩形区域的个数
		NULL,//要清空的矩形数组的首地址
		D3DCLEAR_TARGET /*| D3DCLEAR_ZBUFFER*/, //要清空的内容
		D3DCOLOR_XRGB(255, 255, 255), //要清空成的颜色
		1.0f, //要清空成的深度
		0);//要清空成的模板

	//2. 开始渲染
	if (SUCCEEDED(g_pd3dDevice->BeginScene()))
	{		
		// 3.自己的渲染代码

		//设置资源流
		g_pd3dDevice->SetStreamSource(0, //资源流的数量,单流填0
			g_pVB,//顶点缓冲区
			0,//资源流的起始偏移
			sizeof(CustomVertex));//一个顶点的内存大小
		//设置顶点的灵活格式
		g_pd3dDevice->SetFVF(CUSTOM_VERTEX_FVF);

		//绘制                        图元类型        起始偏移    图元数量
		//g_pd3dDevice->DrawPrimitive(D3DPT_POINTLIST, 0,        3);//点列
		//g_pd3dDevice->DrawPrimitive(D3DPT_LINELIST, 0, 2);//线列
		//g_pd3dDevice->DrawPrimitive(D3DPT_LINESTRIP,0, 3);//线带

		//g_pd3dDevice->DrawPrimitive(D3DPT_TRIANGLELIST, 0,1);//三角形列
		//g_pd3dDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 3);//三角形带
		g_pd3dDevice->DrawPrimitive(D3DPT_TRIANGLEFAN, 0, 3);//三角形扇

		// 4.结束渲染
		g_pd3dDevice->EndScene();
	}

	// 5.翻转
	g_pd3dDevice->Present(NULL, NULL, NULL, NULL);
}




//-----------------------------------------------------------------------------
// Name: MsgProc()
// Desc: The window's message handler
//-----------------------------------------------------------------------------
LRESULT WINAPI MsgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	switch (msg)
	{
	case WM_DESTROY:
		Cleanup();
		PostQuitMessage(0);
		return 0;

	case WM_PAINT:
		Render();
		ValidateRect(hWnd, NULL);//清空绘制区
		return 0;
	}

	return DefWindowProc(hWnd, msg, wParam, lParam);
}




//-----------------------------------------------------------------------------
// Name: wWinMain()
// Desc: The application's entry point
//-----------------------------------------------------------------------------
INT WINAPI wWinMain(HINSTANCE hInst, HINSTANCE, LPWSTR, INT)
{
	//注册窗口类
	WNDCLASSEX wc =
	{
		sizeof(WNDCLASSEX), CS_CLASSDC, MsgProc, 0L, 0L,
		GetModuleHandle(NULL), NULL, NULL, NULL, NULL,
		"D3D Tutorial", NULL
	};
	RegisterClassEx(&wc);

	//创建窗口
	HWND hWnd = CreateWindow("D3D Tutorial", "D3D Tutorial 01: CreateDevice",
		WS_OVERLAPPEDWINDOW, 100, 100, 300, 300,
		NULL, NULL, wc.hInstance, NULL);

	// 初始化D3D环境
	if (SUCCEEDED(InitD3D(hWnd)))
	{
		// 显示更新窗口
		ShowWindow(hWnd, SW_SHOWDEFAULT);
		UpdateWindow(hWnd);

		// 进入主消息循环
		MSG msg;
		while (GetMessage(&msg, NULL, 0, 0))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}

	//注销窗口
	UnregisterClass("D3D Tutorial", wc.hInstance);
	return 0;
}

索引缓冲区

在实际的图形绘制中,除了使用顶点缓冲绘制图形外,还可以使用索引缓冲绘制图形。

索引缓冲由用户定义的,它为每个顶点简历索引值,通常用WORD或者DWORD数组来保存这些索引值,绘制图形时,程序按照顶点索引值的指定顺序绘制图形。

索引缓冲区就是用来存储多边形顶点索引的缓冲区,它指向顶点缓冲区中的顶点位置。

索引缓冲区的意义

当需要渲染的模型的顶点数量特别庞大的时候,一个顶点所占的内存大小往往远超于一个WORD或者DWORD的大小,使用索引缓冲区显然可以节省大量内存,提高整体图形程序的性能。

创建索引缓冲区

HRESULT CreateIndexBuffer(          
    UINT Length,	//索引缓冲区大小,按字节数计算
    DWORD Usage,	//索引缓冲区属性,和顶点缓冲区相同
    D3DFORMAT Format,//索引数组的元素格式,可以是16位或32位的格式
    D3DPOOL Pool,				//索引缓冲区内存位置
    IDirect3DIndexBuffer9** ppIndexBuffer,	//索引缓冲区指针地址
    HANDLE* pHandle		//保留参数,设为0
);

填充顶点缓冲区与保存索引值

/填充索引缓冲区
VOID* pIndices;
if( FAILED( g_pIB->Lock( 0, sizeof(g_Indices), (void**)&pIndices, 0 ) ) )
       return E_FAIL;
}
memcpy( pIndices, g_Indices, sizeof(g_Indices) );
g_pIB->Unlock();

顶点属性设置

使用索引缓冲绘制图形时,同样需要先设置灵活顶点格式,并设置各个顶点的值,还需要定义每个顶点所对应的索引数组的值。

CUSTOMVERTEX g_Vertices[] ={   	
    { 50.0f,  50.0f,   0.5f, 1.0f,  0xffff0000, },     
    { 250.0f,  250.0f,  0.5f, 1.0f,  0xff00ff00, },	
    { 50.0f ,  250.0f,  0.5f, 1.0f,  0xff00ffff, },		
    { 250.0f,  50.0f,   0.5f, 1.0f,  0xffffffff, },     
};
//定义正方形4个顶点的索引
WORD g_Indices[] ={0,1,2,0,3,1};
//索引数组中的元素表示意义为:数组中第0,1,2个元素与第0,3,1个元素分别构成2个三角形。

图形绘制

使用索引缓冲区绘制图形也需要设置资源

g_pd3dDevice->SetStreamSource( 0, g_pVB, 0, sizeof(CUSTOMVERTEX) );

使用索引缓冲区绘制图形时,需要调用函数IDirect3DDevice9::SetIndices()设置当前绘制的索引数组为它的参数。

HRESULT SetIndices(          
      IDirect3DIndexBuffer9 *pIndexData	   //使用的索引缓冲区指针
);

DrawIndexedPrimitive()函数代替DrawPrimitive()函数绘制基本图元。

HRESULT DrawIndexedPrimitive(          
D3DPRIMITIVETYPE Type,		//基本图元的类型
    INT BaseVertexIndex,		//顶点缓冲区的起始位置
    UINT MinIndex,			//最小顶点索引
    UINT NumVertices,		//绘制三角形所用到的顶点
    UINT StartIndex,			//索引缓冲区的起始位置
    UINT PrimitiveCount		//绘制的基本图元的数量
);

释放索引缓冲

与释放顶点缓冲一样,在程序结束前需要调用函数Release()释放索引缓冲。

//-----------------------------------------------------------------------------
// File: main.cpp
//-----------------------------------------------------------------------------
#include 
#include 
#pragma warning( disable : 4996 ) //屏蔽一些警告
#include 
#pragma warning( default : 4996 )

#pragma comment(lib,"d3d9.lib")//引入dx的静态库
#pragma comment(lib,"d3dx9.lib")//引入dx的静态库
#pragma comment(lib,"winmm.lib")//window的函数库

//顶点数据结构体
struct CustomVertex
{
	FLOAT x, y, z;//未经过变换的顶点坐标
	DWORD color;//顶点的颜色
};

//声明顶点灵活格式
#define CUSTOM_VERTEX_FVF (D3DFVF_XYZ | D3DFVF_DIFFUSE)

//-----------------------------------------------------------------------------
// Global variables
//-----------------------------------------------------------------------------
LPDIRECT3D9         g_pD3D = NULL; // 引擎指针,用来创建设备指针
LPDIRECT3DDEVICE9   g_pd3dDevice = NULL; // 设备指针,所有渲染相关的操作全部是用设备指针来完成
LPDIRECT3DVERTEXBUFFER9 g_pVB = NULL; //顶点缓冲区
LPDIRECT3DINDEXBUFFER9 g_pIB = NULL;//索引缓冲区
LPD3DXFONT	 g_pFont = NULL;//字体指针
FLOAT g_fZ = 0.0f;

//初始化顶点数据
HRESULT InitVertex()
{
	/*
				  4 --------------5
				/ |              /|
			   0---------------1|
				| |				| |
				| |				| |
				| |	6___________| |7
				|/______________|/
			   2				3
	*/
	//定义顶点数组
	CustomVertex vertexs[] =
	{
		{ -1, 1, -1, D3DCOLOR_XRGB(255, 0, 0) },//0
		{ 1, 1, -1, D3DCOLOR_XRGB(255, 244, 0) },//1
		{ -1, -1, -1, D3DCOLOR_XRGB(34, 0, 65) },//2
		{ 1, -1, -1, D3DCOLOR_XRGB(64, 34, 0) },//3
		{ -1, 1, 1, D3DCOLOR_XRGB(83, 0, 255) },//4
		{ 1, 1, 1, D3DCOLOR_XRGB(0, 67, 78) },//5
		{ -1, -1, 1, D3DCOLOR_XRGB(3, 0, 64) },//6
		{ 1, -1, 1, D3DCOLOR_XRGB(89, 84, 3) },//7
	};
	//定义索引数组
	WORD indexs[] = {0,1,2,3,7,1,5,0,4,2,6,7,4,5};

	//------------------------------创建顶点缓冲区----------------------------begin
	if (FAILED(g_pd3dDevice->CreateVertexBuffer(sizeof(vertexs), //顶点缓冲区的字节大小
		0, //缓冲区属性,一般填0
		CUSTOM_VERTEX_FVF, //顶点的灵活格式
		D3DPOOL_DEFAULT, //内存类型,一般填D3DPOOL_DEFAULT(默认的)
		&g_pVB,//顶点缓冲区的地址
		0)))
	{
		return E_FAIL;
	}

	CustomVertex* pVertex;
	//填充顶点缓冲区
	//加锁(1.安全处理,2返回顶点缓冲区的首地址)
	if (FAILED(g_pVB->Lock(0,//加锁地址的偏移量
		sizeof(vertexs), //加锁内存的大小
		(VOID**)&pVertex,//加锁内存的首地址
		0)))
	{
		return E_FAIL;
	}

	//复制数据
	memcpy(pVertex, vertexs, sizeof(vertexs));

	//解锁
	if (FAILED(g_pVB->Unlock()))
	{
		return E_FAIL;
	}
	//------------------------------创建顶点缓冲区----------------------------end


	//------------------------------创建索引缓冲区----------------------------begin
	if (FAILED(g_pd3dDevice->CreateIndexBuffer(sizeof(indexs),//索引缓冲区的字节大小
		0,//缓冲区属性,一般填0
		D3DFMT_INDEX16,//每个索引的大小,根据上面的索引数组来决定
		D3DPOOL_DEFAULT, //内存类型,一般填D3DPOOL_DEFAULT(默认的)
		&g_pIB,//索引缓冲区的地址
		0)))
	{
		return E_FAIL;
	}

	WORD* pIndexs;
	//填充索引缓冲区
	//加锁(1.安全处理,2返回索引缓冲区的首地址)
	if (FAILED(g_pIB->Lock(0,//加锁地址的偏移量
		sizeof(indexs), //加锁内存的大小
		(VOID**)&pIndexs,//加锁内存的首地址
		0)))
	{
		return E_FAIL;
	}

	//复制数据
	memcpy(pIndexs, indexs, sizeof(indexs));

	//解锁
	if (FAILED(g_pIB->Unlock()))
	{
		return E_FAIL;
	}
	//------------------------------创建索引缓冲区----------------------------end


	return S_OK;
}


//-----------------------------------------------------------------------------
// Name: InitD3D()
// Desc: 初始化D3D
//-----------------------------------------------------------------------------
HRESULT InitD3D(HWND hWnd)
{
	// 创建D3D引擎指针
	if (NULL == (g_pD3D = Direct3DCreate9(D3D_SDK_VERSION)))
		return E_FAIL;

	//设备参数结构体
	D3DPRESENT_PARAMETERS d3dpp;
	ZeroMemory(&d3dpp, sizeof(d3dpp));//让一段内存归零
	d3dpp.Windowed = TRUE;//是否窗口化
	d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;//交换方式
	d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;//后台缓冲区的格式



	//创建设备指针
	if (FAILED(g_pD3D->CreateDevice(
		D3DADAPTER_DEFAULT, //显示设备的编号
		D3DDEVTYPE_HAL,//设备类型
		hWnd,//窗口句柄
		D3DCREATE_SOFTWARE_VERTEXPROCESSING,//顶点处理方式
		&d3dpp,//设备参数结构体
		&g_pd3dDevice)))//设备指针的地址
	{
		return E_FAIL;
	}

	//初始化顶点数据
	if (FAILED(InitVertex()))
	{
		return E_FAIL;
	}

	//创建字体
	D3DXCreateFont(g_pd3dDevice, 0, 0,//字体宽高,默认填0
		700, //字体粗细,取值范围0~1000
		0,//是否斜体
		0, GB2312_CHARSET, //字符集,英文填ANSI_CHARSET,中文填GB2312_CHARSET
		0, 0, 0, "微软雅黑",//字体名字
		&g_pFont);//字体指针的地址


	//关闭光照
	g_pd3dDevice->SetRenderState(D3DRS_LIGHTING, false);

	//关闭背面拣选
	g_pd3dDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW);

	//开启深度测试
	g_pd3dDevice->SetRenderState(D3DRS_ZENABLE, false);

	//设置深度测试的方式
	g_pd3dDevice->SetRenderState(D3DRS_ZFUNC, D3DCMP_LESS);//默认是小于

	return S_OK;
}




//-----------------------------------------------------------------------------
// Name: Cleanup()
// Desc: Releases all previously initialized objects
//-----------------------------------------------------------------------------
VOID Cleanup()
{
	if (g_pd3dDevice != NULL)
	{
		g_pd3dDevice->Release();
		g_pd3dDevice = NULL;
	}

	if (g_pD3D != NULL)
	{
		g_pD3D->Release();
		g_pD3D = NULL;
	}

	if (g_pVB != NULL)
	{
		g_pVB->Release();
		g_pVB = NULL;
	}

	if (g_pIB != NULL)
	{
		g_pIB->Release();
		g_pIB = NULL;
	}

	if (g_pFont != NULL)
	{
		g_pFont->Release();
		g_pFont = NULL;
	}
}




//-----------------------------------------------------------------------------
// Name: Render()
// Desc: Draws the scene
//-----------------------------------------------------------------------------
VOID Render(float deltaTime)
{
	if (NULL == g_pd3dDevice)
		return;

	//1.清空后台缓冲(其中包括颜色,深度,模板)
	g_pd3dDevice->Clear(0, //要清空的矩形区域的个数
		NULL,//要清空的矩形数组的首地址
		D3DCLEAR_TARGET, //要清空的内容
		D3DCOLOR_XRGB(255, 255, 255), //要清空成的颜色
		1.0f, //要清空成的深度
		0);//要清空成的模板

	//2. 开始渲染
	if (SUCCEEDED(g_pd3dDevice->BeginScene()))
	{
		// 3.自己的渲染代码		
		/*--------------------视图变换------------------------*/
		D3DXMATRIX matView;//视图矩阵
		D3DXMatrixIdentity(&matView);//单位化矩阵
		D3DXVECTOR3 eye(0.0f, 0.0f, -10.0f);//眼睛位置
		D3DXVECTOR3 lookAt(0.0f, 0.0f, 0.0f);//眼睛看向的位置
		D3DXVECTOR3 up(0.0f, 1.0f, 0.0f);//头顶方向
		D3DXMatrixLookAtLH(&matView, &eye, &lookAt, &up);//构造视图矩阵
		g_pd3dDevice->SetTransform(D3DTS_VIEW, &matView);
		/*--------------------投影变换------------------------*/
		D3DXMATRIX matProj;//投影矩阵
		D3DXMatrixIdentity(&matProj);//单位化矩阵
		D3DXMatrixPerspectiveFovLH(&matProj, D3DX_PI / 4.0f, 1.0f, 1.0f, 1000.0f);//构建透视投影矩阵
		g_pd3dDevice->SetTransform(D3DTS_PROJECTION, &matProj);

		//设置资源流
		g_pd3dDevice->SetStreamSource(0, //资源流的数量,单流填0
			g_pVB,//顶点缓冲区
			0,//资源流的起始偏移
			sizeof(CustomVertex));//一个顶点的内存大小

		//设置索引缓冲区
		g_pd3dDevice->SetIndices(g_pIB);

		//设置顶点的灵活格式
		g_pd3dDevice->SetFVF(CUSTOM_VERTEX_FVF);

		/*--------------------世界变换------------------------*/
		D3DXMATRIX matTrans1;//平移矩阵
		D3DXMatrixIdentity(&matTrans1);//单位化矩阵
		D3DXMatrixTranslation(&matTrans1, 0.5f, 0.0f, g_fZ);
		g_pd3dDevice->SetTransform(D3DTS_WORLD, &matTrans1);

		//使用索引缓冲区画图
		g_pd3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLESTRIP,
			0,//顶点缓冲区的起始偏移
			0,//最小索引
			8,//顶点缓冲区的顶点总数
			0,//索引缓冲的起始偏移
			12);//图元数量


		
		static int frame = 0;//一秒钟走了多少帧
		static float fTime = 0.0f;//用来计时的
		static float fps = 0.0f;//FPS

		fTime += deltaTime;
		frame++;
		if (fTime > 1.0f)
		{
			fps = frame / fTime;
			frame = 0;
			fTime = 0.0f;
		}
		RECT rect = { 0, 0, 100, 30 };
		//g_pFont->DrawText(NULL, "中文汉字!!!", -1, &rect,DT_NOCLIP,0xff000000);
		TCHAR str[32];
		sprintf_s(str, "FPS:%f", fps);
		g_pFont->DrawText(NULL, //一般填空
			str,//要显示文字内容
			-1, //要显示的字符数量,全部显示则填-1
			&rect, //文字所在的矩形范围
			DT_NOCLIP,//对齐方式:DT_LEFT等
			0xff000000);//颜色

		// 4.结束渲染
		g_pd3dDevice->EndScene();
	}

	// 5.翻转
	g_pd3dDevice->Present(NULL, NULL, NULL, NULL);
}




//-----------------------------------------------------------------------------
// Name: MsgProc()
// Desc: The window's message handler
//-----------------------------------------------------------------------------
LRESULT WINAPI MsgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	switch (msg)
	{
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	}

	return DefWindowProc(hWnd, msg, wParam, lParam);
}

VOID CheckKeyState()
{
	if (GetKeyState('W') & 0x80)
	{
		g_fZ += 1.0f;
	}
	if (GetKeyState('S') & 0x80)
	{
		g_fZ -= 1.0f;
	}
}


//-----------------------------------------------------------------------------
// Name: wWinMain()
// Desc: The application's entry point
//-----------------------------------------------------------------------------
INT WINAPI wWinMain(HINSTANCE hInst, HINSTANCE, LPWSTR, INT)
{
	//注册窗口类
	WNDCLASSEX wc =
	{
		sizeof(WNDCLASSEX), CS_CLASSDC, MsgProc, 0L, 0L,
		GetModuleHandle(NULL), NULL, NULL, NULL, NULL,
		"D3D Tutorial", NULL
	};
	RegisterClassEx(&wc);

	//创建窗口
	HWND hWnd = CreateWindow("D3D Tutorial", "索引缓冲,字体,深度缓冲",
		WS_OVERLAPPEDWINDOW, 100, 100, 300, 300,
		NULL, NULL, wc.hInstance, NULL);

	// 初始化D3D环境
	if (SUCCEEDED(InitD3D(hWnd)))
	{
		// 显示更新窗口
		ShowWindow(hWnd, SW_SHOWDEFAULT);
		UpdateWindow(hWnd);

		// 进入主消息循环
		MSG msg;
		ZeroMemory(&msg, sizeof(MSG));

		//获取第一次执行时间
		static float s_time1 = timeGetTime() / 1000.0f;

		while (msg.message != WM_QUIT)
		{
			if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
			{
				TranslateMessage(&msg);
				DispatchMessage(&msg);
			}
			//每帧获取当前时间
			float s_time2 = timeGetTime() / 1000.0f;
			if (s_time2 - s_time1 > 1.0f/60)//如果当前时间-上一帧的时间>帧率就刷新
			{
				CheckKeyState();
				Render(s_time2 - s_time1);//两帧之间的时间差
				s_time1 = s_time2;//将当前时间赋值给上一帧的时间
			}			
		}
		Cleanup();
	}

	//注销窗口
	UnregisterClass("D3D Tutorial", wc.hInstance);
	return 0;
}

 

你可能感兴趣的:(DirectX图形学)