Direct3D11學習:(五)演示程序框架


轉載請注明出處:http://www.cnblogs.com/Ray1024

 

一、概述

在此系列最開始的文章Direct3D11學習:(一)開發環境配置中,我們運行了一個例子BoxDemo,看過這個例子源碼的朋友都會發現,代碼量比較大,但是Win32窗口初始化和Direct3D11初始化工作占用了很多大一部分代碼,然而,我們真正關心的繪制代碼並不是這些。

為了避免以后每次創建演示程序都需要重復的初始化工作,把我們的注意力集中在演示程序度所要表達的特定細節上,我們把重復的初始化代碼封裝到一個簡單的程序框架D3D11App中,位於D3D11App.h和D3D11App.cpp文件中。D3D11App.h和D3D11App.cpp文件包含了窗口初始化和D3D11初始化的核心代碼,可以在新的演示程序中包含它,導入該框架,直接編寫我們的核心代碼就可以了。

 

二、演示程序框架

2.1 D3D11Util.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.3 D3D11App部分成員函數介紹

在這節中我們主要介紹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已經實現了它所需要的大部分功能。

有了程序框架之后,我們在學習過程中的演示程序的編寫就簡單多了,直接導入程序框架,只寫我們關心的核心代碼就可以了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM