DirectX3D 框架和基本绘图 [大三TJB_708]

1 文档内容

本文档主要记录在visual studio 2010下配置DirectX SDK (June 2010)后,在MFC程序中如何搭建Direct3D程序框架并介绍基本绘图步骤。

 

2 程序框架

2.1 程序框架总结

Direct3D程序框架可以被分为Direct3D初始化和Direct3D线程:主程序界面(绘图)两部分。

Direct3D初始化步骤

1)        获取IDirect3D9接口:创建IDirect3D9对象并将IDirect3D9接口返回给此对象(或者说此对象实现了IDirect3D9接口)。

2)        获取设备信息,检测硬件顶点处理方式:用接口内中的方法检测显卡设备是否支持硬件顶点处理,若不支持则使用软件顶点处理。

3)        填充D3DPRESENT_PARAMETERS结构:设定将要创建的IDirect3DDevice对象的特性。

4)   创建IDirect3DDevice对象: IDirect3DDevice以创建IDirect3DDevice对象函数的参数返回,通过此对象(给予地址)可以进行绘图。一般是获取所创建对象的地址

 

        由Direct3D初始化步骤可知,最终是要创建一个IDirect3DDevice对象。由于Direct3D符合COM标准,一旦提到“对象”便可想到它是由一种类定义得来,其必包含“数据成员”和“函数成员”。由COM的实质(由C++类描述),被定义的“对象”将会用“数据成员”和“函数成员”表明其属性和操作[比如一个C++形状对象,其内必有标明形状的数据成员,必有实现其形状的函数成员]。如今,我们得到一个IDirect3DDevice对象,它可能具有画笔、颜色种类等数据成员,对应着画图的各种方法。这就是得到的一个对象的目的,只有这个对象才具备D3D某种绘图功能(数据 + 函数)。创建IDirect3DDevice对象之前的步骤是铺垫和准备,对参数的设置对应着D3D的某种特定模式。【这段是个人牢骚,也随便记录点年轻时候的感慨^-^(这个笑脸是别人教的^-^)】

Direct3D主程序界面

        Direct3D程序界面无论是窗体型还是全屏型都需要不断刷新屏幕来显示界面。如此可以设一个循环来不断来刷新屏幕,但对于MFC架构来说,由于封装性我们已经不能将需要不断循环的那部分加入到MFC的主程序循环中了。为此对于MFC Direct3D界面绘图部分,可以单一的开一道线程来实现不断刷新屏幕的需求。

 

2.2 Direct3D 程序框架实现例子

项目框架

        此项目基于MFC对话框,为了简单在创建项目时可以不勾选About box项,建立好项目后。为项目增添新的头文件D3D9App.h和新源程序文件D3D9App.cpp。在对话框头文件中添加IDirect3DDevice指针变量,设为public型,用于指向创建的IDirect3DDevice对象。在D3D9App.h文件中申明D3D初始化程序函数和D3D主程序界面实现函数,在D3D9App.cpp文件中定义相应的函数。在对话框源程序文件的OnInitDialog函数中调用D3D初始化程序,此时将IDirect3DDevice指针变量作为参数传入D3D初始化函数中,伺机获取被创建的IDirect3DDevice对象的地址。将并为D3D主程序界面实现函数开启一道线程,不断刷新D3D界面。

 

Direct3D初始化

        编者可以将Direct3D初始化步骤任意的模块化,只要将需要的接口返回来即可。以下结构将初始化步骤编写在InitD3D9一个函数中。

 

1)获取IDirect3D9接口
LPDIRECT3D9 pD3D9 = NULL;
if( !(pD3D9 = Direct3DCreate9(D3D_SDK_VERSION) ) )
{
	::MessageBox(NULL,  _T("IDirec3D9 object failed"), _T("HI"), MB_OK);
	return E_FAIL;
}


 

      创建IDirect3D9对象的函数为Direct3DCreate9,此函数参数唯一。若函数执行成功则将创建的IDirect3D9对象地址返回。

2)获取设备信息,查询是否支持顶点处理方式

       检测设备信息由IDirect3D9接口函数GetDeviceCaps实现,注意此函数的第三个参数不可设置为“D3DCAPS9 *”类型,否则将会编译出错,此参数将返回设备信息。如果检测到的顶点处理方式记录下来,备作为创建IDirect3DDevice对象时的参数使用。

    作为一个完整的框架,接下来还可以设置D3D程序是为窗体模式还是为全屏模式,如果设置D3D程序为窗体模式则用GetClientRect函数获取D3D程序父窗体的尺寸,此函数的第一个参数需要窗体的句柄(获取一个窗体的句柄有两种方法:一是直接使用此窗体类型对象的数据成员m_hWnd,二是利用GetDlgItem、GetSafeHwnd获取),第二参数即可返回窗体客户区的大小。如果设置为全屏方式则需要用GetSystemMetrics函数获取系统屏幕信息(这里就需要注意句柄的获取了,否则D3D程序初始化有可能会失败)。不管是窗体模式还是全屏模式,目的都是为了获取到D3D窗体或全屏的大小及句柄,以为接下来要设置的D3DD3DPRESENT_PARAMETERS结构做好铺垫。

3)填充D3DPRESENT_PARAMETERS结构

填充此结构时,有三个成员值需要手动获取。

 

成员

对应值

BackBufferWidth

上一步提到的父窗口客户区宽度或全屏宽度

BackBufferHeight

上一步提到的父窗口客户区高度或全屏高度

hDeviceWindow

显示设备输出窗口的句柄(上一步提到的方式可获取),全屏时句柄尚未知如何获取

 

4)创建IDirect3DDevice对象

      创建IDirect3DDevice对象使用CreateDevice函数,此函数是IDirect3D9对象接口函数,第3个参数是显示设备输出窗口的句柄,第3个参数是顶点处理方式,第5个参数是填充好的D3DPRESENT_PARAMETERS结构,第6个参数返回创建的IDirect3DDevice对象的地址,终于获取到IDirect3DDevice对象的地址了。由列举的这几个参数可知,前面几步也是必不可少的,万事具备之时,不仅有只欠东风之时,也有欠CreateDevice函数之点。

好了,如果不出意外,D3D初始化已经完毕了。您可以使用IDirect3DDevice对象来进行D3D图像绘制了。

 

Direct3D主程序界面函数

     由前面提到的D3D界面需要不断刷新之故,在对话框源程序文件中使用AfxBeginThread函数专为D3D绘制函数开启一道线程。然后在此线程中实现D3D界面不断刷新和绘制工作。

 

D3D界面不断刷新实现

    跟其它程序的主程序循环一样,此界面刷新实现依旧采用死循环实现。这似乎是程序图形界面不可缺少的法宝了。

while(1)
{
	HRESULT hr;

	//D3D surface will disappear if has not this code
	if( pDevice )
	{
		//Reports the current cooperative-level status of the Direct3D device
		//for a windowed or full-screen application
		hr = pDevice->TestCooperativeLevel();
		if( FAILED( hr ) )
		{
			if( hr == D3DERR_DEVICELOST )
			{
				continue;
			}
			else if( hr == D3DERR_DEVICENOTRESET )
			{
				//Reset 
			}
				continue;
			}
		}
         if (SUCCEEDED(pDevice->BeginScene()) )
	 {
               //Draw something else
       		pDevice->EndScene();
         }
        pDevice->Present(NULL, NULL, NULL, NULL);
}


      根据D3D界面的“性格”,在刷新D3D数据之前需要用IDirect3DDevice对象接口函数TestCooperativeLevel来检测当前的设备是否可用,并返回系列错误,根据这些错误适当添加一些复位代码就可以将使设备恢复正常,只有等设别恢复正常之后,咱们才可以在IDirect3DDevice对象接口函数BeginSceneEndScene之间Draw something else,如绘制一个三角形,然后还用调用IDirect3DDevice对象接口函数Present来显示界面内容。

 

Direct3D程序三角形绘制

     Direct3D初始化完成了,界面不断刷新实现了,那么展现的数据(图像)在哪里呢?做了那么多不就是为了显示D3D图像的么。画个图入个门吧,如果大家追求安稳性,那还不选三角形。

 

在D3D中画画步骤

1)        准备绘图的数据,并依据准备好的绘图数据信息(大小等)创建顶点缓冲区(所谓缓冲区就是将这些数据存到某个地方吧,不知道的我就猜了再查了)。

2)        访问在缓冲区内存:将准备好的绘图数据存入创建的缓冲区内存中。

3)        设置渲染状态:觉得几何物体怎么样被渲染(?给画好的图涂颜色的方式,我想差不多)

4)        绘制准备:设置资源流(?反正是准备)、设置顶点格式、设置索引缓冲区(若有索引数据)

5)    绘制。

 

D3D画三角形实现例子

1)定义绘图数据并创建顶点缓冲区

还记得C语言中式怎么定义一个结构体的么,里面包含整形,浮点型,这个绘图数据就是这些。

typedef struct CUSTOMVERTEX {
	FLOAT x, y, z;
	FLOAT rhw;
	D3DCOLOR color;
}_CUS_D3D9Vertex;
_CUS_D3D9Vertex CUS_D3D9Vertex[] = {
	{150.0f,50.0f,0.5f,1.0f,D3DCOLOR_XRGB(255, 0, 0)},
	{250.0f,250.0f,0.5f,1.0f,D3DCOLOR_XRGB(0, 255, 0)},
	{50.0f,250.0f,0.5f,1.0f,D3DCOLOR_XRGB(0, 0, 255)}
};


     创建顶点缓冲区肯定是用IDirect3DDevice对象接口函数CreateVertexBuffer(第一个参数是绘图数据在内存中的大小,倒数第2个参数数返回所创缓冲区的地址,所以需要先定义绘图数据)来实现了,我就说这个对象肯定有什么作用。

 

2)访问缓冲区内存

    数据缓冲区已经在上一步创立好了,所以需要访问。当然了是使用IDirect3DDevice对象接口函数Lock访问。然后将绘图数据拷贝到缓冲区中,缓冲区就有了绘图的数据了。

 

3)设置渲染状态

    设置渲染状态使用IDirect3DDevice对象接口函数SetRenderState来实现,就是设置怎么涂图画,参数含义需要读懂手册。这个是不是该看英文功底和计算机素养了。

 

4)绘制准备

A 设置资源流

使用IDirect3DDevice对象接口函数SetStreamSource来设置,此函数的第2个参数是指向缓冲区的指针,第4个参数是缓冲区绘图数据的大小。

 

B 设置顶点格式

设置顶点格式是指根据顶点绘图数据的要求(若顶点绘图数据中包含了颜色,则顶点格式需要包含支持绘制颜色这一项)。参数手册中查看。

 

C设置索引缓冲区

无索引缓冲区,无需设置。

 

5)绘制

    待所有的准备工作都准备好之时就可以调用IDirect3DDevice对象接口函数DrawPrimitive进行绘制了。此接口函数第1个参数表示绘制图形的形式,第2个参数表示绘制图形的起始顶点,第3个参数表示绘制个数。参数含义可自行领会,说得也不一定准确。

         再次强调,绘制三角形的操作是在以下Draw something else 部分完成的。其中pDevice为创建的IDirect3DDevice对象的地址。

if (SUCCEEDED(pDevice->BeginScene()) )
{
       //Draw something else
       pDevice->EndScene();
}
pDevice->Present(NULL, NULL, NULL, NULL);


运行结果

为了体现以下渲染的概念,在这里显示两个运行结果。

 

使用线框模式渲染物体

在设置渲染状态这一步时使用如下语句(pDevice为创建的IDirect3DDevice对象的地址):

pDevice->SetRenderState(D3DRS_FILLMODE,D3DFILL_WIREFRAME);

 

图1 线框模式渲染物体

 

关闭光源渲染图画

同上,在相同的地方添置语句

pDevice->SetRenderState(D3DRS_LIGHTING,FALSE);

 

图 2 关闭光源渲染图画

3 参考文献

Introduction.To.Direct3D9中文版(翁云兵版)^-^还有参考文献呢!

 

此次笔记记录完毕。

 

  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值