初始化Direct3D演示(第四章內容)
初始化Driect3D類:
#include "Common\d3dApp.h"
#include <DirectXColors.h>
using namespace DirectX;
class InitDirect3DApp : public D3DApp
{
public:
InitDirect3DApp(HINSTANCE hInstance);
~InitDirect3DApp();
virtual bool Initialize()override;
private:
virtual void OnResize()override;
virtual void Update(const GameTimer& gt)override;
virtual void Draw(const GameTimer& gt)override;
};
InitDirect3DApp::InitDirect3DApp(HINSTANCE hInscance) :D3DApp(hInscance)
{
}
InitDirect3DApp::~InitDirect3DApp()
{
}
bool InitDirect3DApp::Initialize()
{
if (!D3DApp::Initialize())
{
return false;
}
return true;
}
void InitDirect3DApp::OnResize()
{
D3DApp::OnResize();
}
void InitDirect3DApp::Update(const GameTimer& gt)
{
}
void InitDirect3DApp::Draw(const GameTimer& gt)
{
//重置命令分配器
ThrowIfFailed(mDirectCmdListAlloc->Reset());
//重置命令列表
ThrowIfFailed(mCommandList->Reset(mDirectCmdListAlloc.Get(), nullptr));
//對資源的狀態進行轉變,將資源從呈現狀態轉變到渲染目標狀態
mCommandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(
CurrentBackBuffer(),
D3D12_RESOURCE_STATE_PRESENT,
D3D12_RESOURCE_STATE_RENDER_TARGET
));
//設置視口和裁剪矩形,他們需要跟隨命令列表的重置而重置
mCommandList->RSSetViewports(1, &mScreenViewport);
mCommandList->RSSetScissorRects(1, &mScissorRect);
//清除后台緩沖區和深度緩沖區
mCommandList->ClearRenderTargetView(CurrentBackBufferView(), Colors::LightSteelBlue, 0, nullptr);
mCommandList->ClearDepthStencilView(DepthStencilView(), D3D12_CLEAR_FLAG_DEPTH | D3D12_CLEAR_FLAG_STENCIL,
1.0f, 0, 0, nullptr);
//指定將要渲染的緩沖區
mCommandList->OMSetRenderTargets(1, &CurrentBackBufferView(), true, &DepthStencilView());
//再次對資源狀態進行轉變,將資源從渲染目標狀態轉變為呈現狀態
mCommandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(
CurrentBackBuffer(), D3D12_RESOURCE_STATE_RENDER_TARGET,
D3D12_RESOURCE_STATE_PRESENT
));
//完成命令的記錄
ThrowIfFailed(mCommandList->Close());
//將命令列表的命令加入到命令隊列中
ID3D12CommandList* cmdsList[] = { mCommandList.Get() };
mCommandQueue->ExecuteCommandLists(_countof(cmdsList), cmdsList);
//交換前后台緩沖區
ThrowIfFailed(mSwapChain->Present(0, 0));;
mCurrBackBuffer = (mCurrBackBuffer + 1) % SwapChainBufferCount;
//等待此幀的命令執行完畢
FlushCommandQueue();
}
主函數:
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
try
{
InitDirect3DApp theApp(hInstance);
if (!theApp.Initialize())
{
return 0;
}
return theApp.Run();
}
catch (DxException& e)
{
MessageBox(nullptr, e.ToString().c_str(), L"HR Failed", MB_OK);
return 0;
}
}
運行結果
接下來我們將介紹一些在前面沒有提到的方法:
1、ClearRenderTargetView():將指定的渲染目標清理為給定的顏色
2、ClearDepthStencilView():清理指定的深度/模板緩沖區
3、OMSetRenderTargets():設置我們希望在渲染流水線上使用的渲染目標和深度/模板緩沖區
調試Direct3D應用程序
大多數的Direct3D函數會返回HRESULT錯誤碼,我們的示例程序則采用簡單的錯誤處理機制檢測返回的HRESULT值,如果檢測失敗。則拋出異常,顯示調用出錯的錯誤碼,函數名,文件名以及發生錯誤的行號,這些操作都由d3dUtil.h中的代碼實現:
class DxException
{
public:
DxException() = default;
DxException(HRESULT hr, const std::wstring& functionName, const std::wstring& filName, int lineNumber);
std::wstring ToString()const;
HRESULT ErrorCode = S_OK;
std::wstring FunctionName;
std::wstring FileName;
int LineNumber = -1;
};
#ifndef ThrowIfFailed
#define ThrowIfFailed(x) \
{ \
HRESULT hr__ = (x); \
std::wstring wfn = AnsiToWString(__FILE__); \
if(FAILED(hr__)) { throw DxException(hr__, L#x, wfn, __LINE__); } \
}
#endif
由上面的代碼可以看出來,ThrowifFailed是一個宏而不是一個函數,如果ThrowifFailed是一個函數,那么__FILE__和__LINE__將會定位到ThrowifFailed函數所在的文件和行,而不是出錯函數的文件和行。
L#x會將ThrowifFailed的參數轉換為Unicode字符串,這樣一來,我們就可以將函數調用的錯誤信息輸出到消息框中。
第四章小結
- Direct3D是一種介於程序員和硬件之間的橋梁,通過這個橋梁,程序員可以通過調用Direct3D函數來實現把資源視圖綁定到硬件渲染流水線中,配置渲染流水線的輸出以及繪制3D幾何體等操作
- 組件對象模型(COM)是一種可以使DirectX不依賴任何特定語言且向后兼容的技術
- 1D、2D、3D紋理類似於有數據元素構成的1D、2D、3D數組,紋理元素的格式必定是DXGI_FORAMT枚舉類型中的其中一種,紋理除了可以存儲圖像數據之外,還可以存儲想深度信息等其他類型的數據
- 前台緩沖區和后台緩沖區構成了交換鏈,交換鏈在代碼中可以通過IDXGISwapChain接口表示,使用兩個緩沖區的情況稱之為雙緩沖,交換鏈的存在可以避免出現動畫中出現閃爍的問題(前台緩沖區和后台緩沖區互換的操作稱之為呈現)
- 深度緩沖是一種用於確定物體在場景中離攝像機最近點的技術,通過深度緩沖,我們可以不用考慮物體在場景中繪制的先后順序
- 在Driect3D中,資源不可以直接和渲染流水線相互綁定,因此我們需要為繪制調用時所引用的資源指定描述符,描述符可以視為GPU識別以及描述資源的一種輕量級結構體,我們還可以為一種資源創建不同的描述符,這樣我們就可以讓一種資源具有多種用途。應用程序可以通過創建描述符堆來存儲描述符。
- ID3DDeviec是Direct3D最重要的接口,我們可以把它看作是圖形硬件設備的軟件控制器,我們可以通過ID3DDevice來創建GPU資源以及其他一些用於控制圖形硬件設備的特定接口
- 每一個GPU都有一個命令隊列,CPU可以通過Direct3D API用命令列表來向該隊列提交命令,這些命令將指揮GPU進行各種操作,在命令沒有到達隊列首部之前,該命令是不會被執行的。
- GPU是系統中和CPU一起並行工作的第二種處理器,我們需要對CPU和GPU進行同步
- 性能計數器是一種高精度的計時器,它是測量微小時間差的一種有效工具,性能計數器使用的時間單位是計數,QueryPerformanceFrequency函數輸出的是性能計數器每秒的計數,可以用它將計數單位轉換為秒,QueryPerformanceCounter函數輸出的是當前的時間值(以計數為單位)
- 通過統計時間段t內處理的幀數可以計算出每秒的平均幀數(FPS),不過為了更直觀的對程序的性能進行考量,我們一般使用“處理一幀所花費的時間”這個統計信息。以秒為單位的每幀平均處理時間可以用幀率的倒數來進行計算,即:1/FPS