Direct3D11学习:(五)演示程序框架

摘要:
所有的D3D11应用程序类都继承于D3D11App类,重载它的virtual框架函数,并创建一个D3D11App派生类的单例对象。所谓框架方法,就是D3D11App中的虚函数,在之后学习的每个演示程序中,我们可以重载这些方法来实现特定示例中的代码细节。D3D11App类实现的这种结构可以将所有的初始化代码、消息处理代码和其他代码安排得井井有条,使派生类专注于实现演示程序的特定代码。

转载请注明出处:http://www.cnblogs.com/Ray1024

一、概述

在此系列最开始的文章Direct3D11学习:(一)开发环境配置中,我们运行了一个例子BoxDemo,看过这个例子源码的朋友都会发现,代码量比较大,但是Win32窗口初始化和Direct3D11初始化工作占用了很多大一部分代码,然而,我们真正关心的绘制代码并不是这些。

为了避免以后每次创建演示程序都需要重复的初始化工作,把我们的注意力集中在演示程序度所要表达的特定细节上,我们把重复的初始化代码封装到一个简单的程序框架D3D11App中,位于D3D11App.h和D3D11App.cpp文件中。D3D11App.h和D3D11App.cpp文件包含了窗口初始化和D3D11初始化的核心代码,可以在新的演示程序中包含它,导入该框架,直接编写我们的核心代码就可以了。

二、演示程序框架

2.1D3D11Util.h文件

在介绍演示程序框架之前,我们需要介绍一组文件D3D11Util.h和D3D11Util.cpp。

这组文件包含了一些有用的工具代码,都是程序中常用的工具。比如COM对象安全释放的宏ReleaseCOM()、HRESULT值的错误处理宏HR()和常用颜色值定义等等。

这里解释一下HRESULT值的错误处理宏HR():

#ifndef HR
#define HR(x)                                              
{                                                          
	HRESULT hr = (x);                                      
	if(FAILED(hr))                                         
	{                                                      
		DXTrace(__FILE__, (DWORD)__LINE__, hr, L#x, true); 
	}                                                      
}
#endif

在这个宏中,使用了DX的错误处理库dxerr.lib库中的函数DXTrace。当函数的返回值表明调用失败时,我们把返回值传递给DXTrace函数。如果hr=S_FALSE时,弹出消息框显示错误信息。

HR()宏使用方法:

HR(m_pD3DDevice->CheckMultisampleQualityLevels(
		DXGI_FORMAT_R8G8B8A8_UNORM, 4, &m_4xMsaaQuality));

D3D11Util.h和D3D11Util.cpp文件中其他的工具在这里就不介绍了,有兴趣的朋友可以在文章最后的下载源码处下载浏览源码。

2.2核心类D3D11App

D3D11App是我们学习D3D11中所有应用程序类的基类,它提供了用于创建主应用程序窗口、运行应用程序消息循环、处理窗口消息和初始化D3D11的函数。另外,这个类还定义了一些框架函数。所有的D3D11应用程序类都继承于D3D11App类,重载它的virtual框架函数,并创建一个D3D11App派生类的单例对象。D3D11App类的定义如下:

class D3D11App
{
public:
	D3D11App(HINSTANCE hInstance);
	virtual ~D3D11App();
	
	// 获取应用程序实例句柄
	HINSTANCE AppInst()const;
	// 获取主窗口句柄
	HWND MainWnd()const;
	// 后台缓存区的长宽比
	float AspectRatio()const;
	// 应用程序消息循环
	int Run();
 
	// 框架方法
	// 派生类需要重载这些方法实现所需的功能

	virtual bool Init();
	virtual void OnResize(); 
	virtual void UpdateScene(float dt)=0;
	virtual void DrawScene()=0; 
	virtual LRESULT MsgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);

	// 处理鼠标输入事件的便捷重载函数
	virtual void OnMouseDown(WPARAM btnState, int x, int y){ }
	virtual void OnMouseUp(WPARAM btnState, int x, int y)  { }
	virtual void OnMouseMove(WPARAM btnState, int x, int y){ }

protected:

	// 创建窗口
	bool InitMainWindow();

	// 初始化D3D
	bool InitDirect3D();

	// 计算帧率
	void CalculateFrameStats();

protected:

	HINSTANCE m_hAppInst;		// 应用程序实例句柄
	HWND      m_hMainWnd;		// 主窗口句柄
	bool      m_appPaused;		// 程序是否处在暂停状态
	bool      m_minimized;		// 程序是否最小化
	bool      m_maximized;		// 程序是否最大化
	bool      m_Resizing;		// 程序是否处在改变大小的状态
	UINT      m_4xMsaaQuality;	// 4X MSAA质量等级

	GameTimer m_timer;			// 用于记录deltatime和游戏时间

	ID3D11Device*			m_pD3DDevice;			// D3D11设备
	ID3D11DeviceContext*	m_pD3DImmediateContext;	// 上下文
	IDXGISwapChain*			m_pSwapChain;			// 交换链
	ID3D11Texture2D*		m_pDepthStencilBuffer;	// 深度缓冲区
	ID3D11RenderTargetView* m_pRenderTargetView;	// 渲染目标视图
	ID3D11DepthStencilView* m_pDepthStencilView;	// 深度缓冲视图	
	D3D11_VIEWPORT			m_screenViewport;		// 视口


	std::wstring	m_mainWndCaption;		// 窗口标题
	D3D_DRIVER_TYPE m_D3DDriverType;		// 是否使用硬件加速
	int				m_clientWidth;			// 窗口大小
	int				m_clientHeight;			// 窗口大小
	bool			m_enable4xMsaa;			// 是否使用4XMSAA
};

上面的代码中有注释,从注释中我们可以看到成员函数和变量的作用。

下面的部分我们将详细介绍D3D11App一部分成员函数。

2.3D3D11App部分成员函数介绍

在这节中我们主要介绍D3D11App类的成员函数中的框架方法。所谓框架方法,就是D3D11App中的虚函数,在之后学习的每个演示程序中,我们可以重载这些方法来实现特定示例中的代码细节。D3D11App类实现的这种结构可以将所有的初始化代码、消息处理代码和其他代码安排得井井有条,使派生类专注于实现演示程序的特定代码。下面是对这些框架方法的描述:

(1)Init:该方法包含应用程序的初始化代码,比如分配资源、初始化对象和设置灯光。该方法在D3D11App的实现中包含InitMainWindow和InitDirect3D方法的调用语句;所以,当在派生类中重载该方法时,应首先调用该方法的D3D11App版本,就像下面这样:

void TestApp::Init() 
{
    if(!D3D11App::Init())
        return false;
    /* 剩下的初始化代码从这里开始 */
}

(2)OnResize:该方法在D3D11App::MsgProc收到WM_SIZE消息时调用。当窗口的尺寸改变时,一些与客户区大小相关的Direct3D属性也需要改变。尤其是需要重新创建后台缓冲区和深度/模板缓冲区,使它们与窗口客户区的大小一致。后台缓冲区的大小可以通过调用IDXGISwapChain::ResizeBuffers方法来进行调整。而深度/模板缓冲区必须被销毁,然后根据新的大小重新创建。另外,渲染目标视图和深度/模板视图也必须重新创建。OnResize方法在D3D11App的实现中包含了调整后台缓冲区和深度/模板缓冲区的代码;详情请直接参见源代码。除缓冲区外,依赖于客户区大小的其他属性(例如,投影矩阵)也必须重新创建。我们把该方法作为框架的一部分是因为当窗口大小改变时,客户代码可能需要执行一些它自己的逻辑。

(3)UpdateScene:该抽象方法每帧都会调用,用于随着时间更新3D应用程序(例如,实现动画和碰撞检测、检查用户输入、计算每秒帧数等等)。

(4)DrawScene:该抽象方法每帧都会调用,用于将3D场景的当前帧绘制到后台缓冲区。当绘制当前帧时,我们调用了IDXGISwapChain::Present方法将后台缓冲区的内容呈现在屏幕上。

(5)MsgProc:该方法是主应用程序窗口的消息处理函数。通常,当你只需重载该方法,就可以处理未由D3D11App::MsgProc处理(或者没按照你所希望的方式处理)的消息。如果你重载了这个方法,那么那些你没有处理的消息都会送到D3D11App::MsgProc中进行处理。

另外,除了上述的五个框架方法之外,为了使用起来更方便,我们还提供了三个虚函数,用于处理鼠标点击、释放和移动的事件。

virtual void OnMouseDown(WPARAM btnState, int x, int y){ }
virtual void OnMouseUp(WPARAM btnState, int x, int y)  { }
virtual void OnMouseMove(WPARAM btnState, int x, int y){ }

你可以重载这些方法处理鼠标事件,而用不着重载MsgProc方法。这些方法的第一个参数WPARAM都是相同的,保存了鼠标按键的状态(例如,哪个鼠标按键被按下),第二、三个参数是光标在客户区域的(x,y)坐标。

2.4将程序框架导入新建演示程序

这个框架还需要另外一个部分:游戏计时器,上一篇文章我们介绍过了游戏计时器的实现,这里就不提了。

我们将上面提到的三组文件(D3D11Util.h和D3D11Util.cpp、D3D11App.h和D3D11App.cpp、GameTimer.h和GameTimer.cpp)放到一个文件夹Common中,这样每个演示程序都可以使用,避免多次复制了。我们的演示程序需要使用程序框架时,需要导入框架,即把Common文件夹中的文件添加到演示程序中,并把Common文件夹的目录添加到演示程序项目属性的包含目录中。

这样,程序框架就成功地导入到新建演示程序中了,我们接下来就可以使用程序框架编写演示程序了。

2.5使用框架编写演示程序

(1)创建一个继承自主框架类D3D11App的类TestApp:

class TestApp : public D3D11App
{
public:
	TestApp(HINSTANCE hInstance);

	void UpdateScene(float dt);
	void DrawScene(); 
};

(2)添加TestApp类的成员函数实现

//
// TestApp Implement
//

TestApp::TestApp(HINSTANCE hInstance)
	: D3D11App(hInstance)
{
	m_mainWndCaption = L"2_D3DTimingAndAnimation";
}

void TestApp::UpdateScene(float dt)
{

}

void TestApp::DrawScene()
{
	assert(m_pD3DImmediateContext);
	assert(m_pSwapChain);

	m_pD3DImmediateContext->ClearRenderTargetView(m_pRenderTargetView, reinterpret_cast<const float*>(&Colors::LightSteelBlue));
	m_pD3DImmediateContext->ClearDepthStencilView(m_pDepthStencilView, D3D11_CLEAR_DEPTH|D3D11_CLEAR_STENCIL, 1.0f, 0);

	HR(m_pSwapChain->Present(0, 0));
}

(3)添加程序入口

// 程序入口
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE prevInstance,
	PSTR cmdLine, int showCmd)
{
#if defined(DEBUG) | defined(_DEBUG)
	_CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
#endif

	TestApp theApp(hInstance);

	if( !theApp.Init() )
		return 0;

	return theApp.Run();
}

到此为止,我们已经利用程序框架成功地生成了一个演示程序。

有兴趣看源码的朋友可以点击这里下载,源码为3_D3DFrame文件夹。

三、结语

基本上,我们用不着做任何实际工作就可以实现这个程序,因为基类D3D11App已经实现了它所需要的大部分功能。

有了程序框架之后,我们在学习过程中的演示程序的编写就简单多了,直接导入程序框架,只写我们关心的核心代码就可以了。

免责声明:文章转载自《Direct3D11学习:(五)演示程序框架》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇django数据导入用友面试下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Vector的用法

vector容器是笔试时最经常用到的容器,它实际是一个类模板,它所具有一些成员函数我们必须熟练使用,这样才会加快编程速度。 首先加头文件并定义: #include<vector> vector<int>vec; 1、获取vector里的元素个数 int len =vec.size(); 2、向vector添加元素 int temp;...

VB可变数组定义

关键点:Dim str() As string '定义redim str(n) '重设长度 or redim Preserve str(n) '重设长度,并保持原来记录不变 详细阐述:ReDim 语句 在过程级别中使用,用于为动态数组变量重新分配存储空间。 语法 ReDim [Preserve] varname(subscripts) [As type]...

VxWorks固件分析方法总结

VxWorks固件分析方法总结 最近研究基于VxWorks系统的iot设备的固件分析方法,将学习心得记录于此,以备将来的查询需要。 1.VxWorks是什么? VxWorks是美国WindRiverSystem公司(风河公司)推出的一个RTOS(实时操作系统),凭借良好的持续发展能力、高性能的内核以及友好的用户开发环境,在嵌入式实时操作系统领域占据一席之地...

Go-结构体

Go语言中没有“类”的概念,也不支持“类”的继承等面向对象的概念。Go语言中通过结构体的内嵌再配合接口比面向对象具有更高的扩展性和灵活性。 类型别名和自定义类型 自定义类型 在Go语言中有一些基本的数据类型,如string、整型、浮点型、布尔等数据类型,Go语言中可以使用type关键字来定义自定义类型。 自定义类型是定义了一个全新的类型。我们可以基于内置的...

Python 操作 MySQL 的5种方式

不管你是做数据分析,还是网络爬虫,Web 开发、亦或是机器学习,你都离不开要和数据库打交道,而 MySQL 又是最流行的一种数据库,这篇文章介绍 Python 操作 MySQL 的5种方式,你可以在实际开发过程中根据实际情况合理选择。 1、MySQLdb MySQLdb又叫MySQL-python ,是 Python 连接 MySQL 最流行的一个驱动,很...

bonding的系统初始化介绍

bond0模块的加载 Bonding原理 为方便理解bonding的配置及实现,顺便阐述一下Linux的网络接口及其配置文件。在 Linux 中,所有的网络通讯都发生在软件接口与物理网络设备之间。与网络接口配置相关的文件,以及控制网络接口状态的脚本文件,全都位于 /etc/sysconfig/netwrok-scripts/ 目录下。网络接口配置文件用于控...