DirectX11 Direct3D初始化
Direct3D初始化
1. Direct3D初始化步驟
下面將講解如何初始化Direct3D。我們將Direct3D的初始化過程分為如下幾個步驟:
1.使用D3D11CreateDevice方法創建ID3D11Device和ID3D11DeviceContext。
2.使用ID3D11Device::CheckMultisampleQualityLevels方法檢測設備支持的4X多重采樣質量等級。
3.填充一個IDXGI_SWAP_CHAIN_DESC結構體,該結構體描述了所要創建的交換鏈的特性。
4.查詢IDXGIFactory實例,這個實例用于創建設備和一個IDXGISwapChain實例。
5.為交換鏈的后臺緩沖區創建一個渲染目標視圖。
6.創建深度/模板緩沖區以及相關的深度/模板視圖。
7.將渲染目標視圖和深度/模板視圖綁定到渲染管線的輸出合并階段,使它們可以被Direct3D使用。
8.設置視口。
2. 創建設備(Device)和上下文(Context)
要初始化Direct3D,首先需要創建Direct3D 11設備(ID3D11Device)和上下文(ID3D11DeviceContext)。它們是是最重要的Direct3D接口,可以被看成是物理圖形設備硬件的軟控制器;也就是說,我們可以通過該接口與硬件進行交互,命令硬件完成一些工作(比如:在顯存中分配資源、清空后臺緩沖區、將資源綁定到各種管線階段、繪制幾何體)。具體而言:
1. ID3D11Device接口用于檢測顯示適配器功能和分配資源。
2. ID3D11DeviceContext接口用于設置管線狀態、將資源綁定到圖形管線和生成渲染命令。
設備和上下文可用如下函數創建:
HRESULT D3D11CreateDevice (IDXGIAdapter *pAdapter,D3D_DRIVER_TYPE DriverType,HMODULE Software ,UINT Flags ,CONST D3D_FEATURE_LEVEL *pFeatureLevels ,UINT FeatureLevels ,UINT SDKVersion,ID3D11Device **ppDevice ,D3D_FEATURE_LEVE L *pFeatureLevel,ID3D11DeviceContext **ppImmediateContext );1.pAdapter:指定要為哪個物理顯卡創建設備對象。當該參數設為空值時,表示使用主顯卡。在本書的示例程序中,我們只使用主顯卡。
2.DriverType:一般來講,該參數總是指定為D3D_DRIVER_TYPE_HARDWARE,表示使用3D硬件來加快渲染速度。但是,也可以有兩個其他選擇:
D3D_DRIVER_TYPE_REFERENCE:創建所謂的引用設備(reference device)。引用設備是Direct3D的軟件實現,它具有Direct3D的所有功能(只是運行速度非常慢,因為所有的功能都是用軟件來實現的)。引用設備隨DirectX SDK一起安裝,只用于程序員,而不應該用于程序發布。使用引用設備有兩個原因:
測試硬件不支持的代碼;例如,在一塊不支持Direct3D 11的顯卡上測試一段Direct3D 11的代碼。
測試驅動程序缺陷。當代碼能在引用設備上正常運行,而不能在硬件上正常工作時,說明硬件的驅動程序可能存在缺陷。
D3D_DRIVER_TYPE_SOFTWARE:創建一個用于模擬3D硬件的軟件驅動器。要使用軟件驅動器,你必須自己創建一個,或使用第三方的軟件驅動器。與下面要說的WARP驅動器不同,Direct3D不提供軟件驅動器。
D3D_DRIVER_TYPE_WARP:創建一個高性能的Direct3D 10.1軟件驅動器。WARP代表Windows Advanced Rasterizati on Platform。因為WARP不支持Direct3D 11,因此我們對它不感興趣。
3.Software:用于支持軟件光柵化設備(software rasterizer)。我們總是將該參數設為空值,因為我們使用硬件進行渲染。如果讀者想要使用這一功能,那么就必須先安裝一個軟件光柵化設備。
4.Flags:可選的設備創建標志值。當以release模式生成程序時,該參數通常設為0(無附加標志值);當以debug模式生成程序時,該參數應設為:
D3D11_CREATE_DEVICE_DEBUG:用以激活調試層。當指定調試標志值后,Direct3D會向VC++的輸出窗口發送調試信息;圖4.6展示了輸出錯誤信息的一個例子。
(Direct3D 11調試輸出的一個例子)
5.pFeatureLevels:D3D_FEATURE_LEVEL數組,元素的順序表示要特征等級(見§4.1.9)的測試順序。將這個參數設置為null表示選擇可支持的最高等級。
6.FeatureLevels:pFeatureLevels數組中的元素D3D_FEATURE_LEVELs的數量,若pFeatureLevels設置為null,則這個值為0。
7.SDKVersion:始終設為D3D11_SDK_VERSION。
8.ppDevice:返回創建后的設備對象。
9.pFeatureLevel:返回pFeatureLevels數組中第一個支持的特征等級(如果pFeatureLevels 為null,則返回可支持的最高等級)。
10.ppImmediateContext:返回創建后的設備上下文。
下面是調用該函數的一個示例:
UINT createDeviceFlags = 0;#if defined(DEBUG)||defined(_DEBUG)createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG; #endifD3D_FEATURE_LEVEL featureLevel; ID3D11Device * md3dDevice; ID3D11Device Context* md3dImmediate Context; HRESULT hr = D3D11CreateDevice(0, // 默認顯示適配器D3D_DRIVER_TYPE_HARDWARE ,0, // 不使用軟件設備createDeviceFlags ,0, 0, // 默認的特征等級數組D3D11_SDK_VERSION,& md3dDevice ,& featureLevel,& md3dImmediateContext); if(FAILED(hr) ) {MessageBox(0, L"D3D11CreateDevice Failed.", 0, 0);return false ; } if(featureLevel != D3D_FEATURE_LEVEL_11_0) {MessageBox(0, L"Direct3D FeatureLevel 11 unsupported.", 0, 0);return false; }從上面的代碼可以看到我們使用的是立即執行上下文(immediate context):
ID3D11DeviceContext* md3dImmediateContext;
還有一種上下文叫做延遲執行上下文(ID3D11Device::CreateDeferredContext)。該上下文主要用于Direct3D 11支持的多線程程序。多線程編程是一個高級話題,本書并不會介紹,但下面介紹一點基本概念:
1.在主線程中使用立即執行上下文。
2.在工作線程總使用延遲執行上下文。
(a)每個工作線程可以將圖形指令記錄在一個命令列表(ID3D11CommandList)中。
(b)隨后,每個工作線程中的命令列表可以在主渲染線程中加以執行。
在多核系統中,可并行處理命令列表中的指令,這樣可以縮短編譯復雜圖形所需的時間。
3. 檢測4X多重采樣質量支持
創建了設備后,我們就可以檢查4X多重采樣質量等級了。所有支持Direct3D 11的設備都支持所有渲染目標格式的4X MSAA(支持的質量等級可能并不相同)。
因為4X MSAA總是被支持的,所以返回的質量等級總是大于0。
4. 描述交換鏈
下一步是創建交換鏈,首先需要填充一個DXGI_SWAP_CHAIN_DESC結構體來描述我們將要創建的交換鏈的特性。該結構體的定義如下:
DXGI_MODE_DESC類型是另一個結構體,其定義如下:
typedef struct DXGI_MODE_DESC {UINT Width; // 后臺緩沖區寬度UINT Height; // 后臺緩沖區高度DXGI_RATIONAL RefreshRate; // 顯示刷新率DXGI_FORMAT Format; // 后臺緩沖區像素格式DXGI_MODE_SCANLINE_ORDER ScanlineOrdering;// display scanline modeDXGI_MODE_SCALING Scaling; // display scaling mode } DXGI_MODE_DESC;注意:在下面的數據成員描述中,我們只涵蓋了一些常用的標志值和選項,它們對于初學者來說非常重要。對于其他標志值和選項的描述,請參閱SDK文檔。
1.BufferDesc:該結構體描述了我們所要創建的后臺緩沖區的屬性。我們主要關注的屬性有:寬度、高度和像素格式;其他屬性的詳情請參閱SDK文檔。
2.SampleDesc:多重采樣數量和質量級別(參閱4.1.8節)。
3.BufferUsage:設為DXGI_USAGE_RENDER_TARGET_OUTPUT,因為我們要將場景渲染到后臺緩沖區(即,將它用作渲染目標)。
4.BufferCount:交換鏈中的后臺緩沖區數量;我們一般只用一個后臺緩沖區來實現雙緩存。當然,你也可以使用兩個后臺緩沖區來實現三緩存。
5.OutputWindow:我們將要渲染到的窗口的句柄。
6.Windowed:當設為true時,程序以窗口模式運行;當設為false時,程序以全屏(full-screen)模式運行。
7.SwapEffect:設為DXGI_SWAP_EFFECT_DISCARD,讓顯卡驅動程序選擇最高效的顯示模式。
8.Flags:可選的標志值。如果設為DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH,那么當應用程序切換到全屏模式時,Direct3D會自動選擇與當前的后臺緩沖區設置最匹配的顯示模式。如果未指定該標志值,那么當應用程序切換到全屏模式時,Direct3D會使用當前的桌面顯示模式。我們在示例框架中沒有使用該標志值,因為對于我們的演示程序來說,在全屏模式下使用當前的桌面顯示模式可以得到很好的效果。
下面是在我們的示例框架中填充DXGI_SWAP_CHAIN_DESC結構體的代碼:
DXGI_SWAP_CHAIN_DESC sd; sd.BufferDesc.Width = mClientWidth; // 使用窗口客戶區寬度 sd.BufferDesc.Height = mClientHeight; sd.BufferDesc.RefreshRate.Numerator = 60; sd.BufferDesc.RefreshRate.Denominator = 1; sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; sd.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED; sd.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED; // 是否使用4X MSAA? if(mEnable4xMsaa) {sd.SampleDesc.Count = 4;// m4xMsaaQuality是通過CheckMultisampleQualityLevels()方法獲得的sd.SampleDesc.Quality = m4xMsaaQuality-1; } // NoMSAA else {sd.SampleDesc.Count = 1;sd.SampleDesc.Quality = 0; } sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; sd.BufferCount = 1; sd.OutputWindow = mhMainWnd; sd.Windowed = true; sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; sd.Flags = 0;注意:
1.如果你想在運行時改變多重采樣的設置,那么必須銷毀然后重新創建交換鏈。
2.因為大多數顯示器不支持超過24位以上的顏色,再多的顏色也是浪費,所以我們將后臺緩沖區的像素格式設置為DXGI_FORMAT_R8G8B8A8_UNORM(紅、綠、藍、alpha各8位)。額外的8位alpha并不會輸出在顯示器上,但在后臺緩沖區中可以用于特定的用途。
5. 創建交換鏈
交換鏈(IDXGISwapChain)是通過IDXGIFactory實例的IDXGIFactory::CreateSwapChain方法創建的:
我們可以通過CreateDXGIFactory(需要鏈接dxgi.lib)獲取指向一個IDXGIFactory實例的指針。但是使用這種方法獲取IDXGIFactory實例,并調用IDXGIFactory::CreateSwapChain方法后,會出現如下的錯誤信息:
DXGI Warning: IDXGIFactory::CreateSwapChain: This function is being called with a device from a different IDXGIFactory.
要修復這個錯誤,我們需要使用創建設備的那個IDXGIFactory實例,要獲得這個實例,必須使用下面的COM查詢(具體解釋可參見IDXGIFactory的文檔):
IDXGIDevice * dxgiDevice = 0; HR(md3dDevice ->QueryInterface(__uuidof(IDXGIDevice),(void**)&dxgiDevice )); IDXGIAdapter* dxgiAdapter = 0; HR(dxgiDevice ->GetParent(__uuidof(IDXGIAdapter),(void**))&dxgiAdapte r )); // 獲得IDXGIFactory 接口 IDXGIFactory* dxgiFactory = 0; HR(dxgiAdapter->GetParent(__uuid of(IDXGIFactory),(void**))&dxgiFactor y)); // 現在,創建交換鏈 IDXGISwapChain* mSwapChain; HR(dxgiFactory->CreateSwapChain(md3dDevice, &sd , &mSw ap Chain); // 釋放COM接口 ReleaseCOM (dxgiDevice ; ReleaseCOM (dxgiAdapter); ReleaseCOM (dxgiFactory);DXGI(DirectX Graphics Inf rastructure)是獨立于Direct3D的API,用于處理與圖形關聯的東西,例如交換鏈等。DXGI與Direct3D分離的目的在于其他圖形API(例如Direct2D)也需要交換鏈、圖形硬件枚舉、在窗口和全屏模式之間切換,通過這種設計,多個圖形API都能使用DXGI API。
補充:你也可以使用D3D11CreateDeviceAndSwapChain方法同時創建設備、設備上下文和交換鏈。
6. 創建渲染目標視圖
如4.1.6節所述,資源不能被直接綁定到一個管線階段;我們必須為資源創建資源視圖,然后把資源視圖綁定到不同的管線階段。尤其是在把后臺緩沖區綁定到管線的輸出合并器階段時(使Direct3D可以在后臺緩沖區上執行渲染工作),我們必須為后臺緩沖區創建一個渲染目標視圖(render target view)。下面的代碼說明了一實現過程:
1.IDXGISwapChain::GetBuffer方法用于獲取一個交換鏈的后臺緩沖區指針。該方法的第一個參數表示所要獲取的后臺緩沖區的索引值(由于后臺緩沖區的數量可以大于1,所以這里必須指定索引值)。在我們的演示程序中,我們只使用一個后臺緩沖區,所以該索引值設為0。第二個參數是緩沖區的接口類型,它通常是一個2D紋理(ID3D11Texture2D)。第三個參數返回指向后臺緩沖區的指針。
2.我們使用ID3D11Device::CreateRenderTargetView方法創建渲染目標視圖。
第一個參數指定了將要作為渲染目標的資源,在上面的例子中,渲染目標是后臺緩沖區(即,我們為后臺緩沖區創建了一個渲染目標視圖)。
第二個參數是一個指向D3D11_RENDER_TARGET_VIEW_DESC結構體的指針,該結構體描述了資源中的元素的數據類型。如果在創建資源時使用的是某種強類型格式(即,非弱類型格式),則該參數可以為空,表示以資源的第一個mipmap層次(后臺緩沖區也只有一個mipmap層次)作為視圖格式。第三個參數通過指針返回了創建后的渲染目標視圖對象。
3.每調用一次IDXGISwapChain::GetBuffer方法,后臺緩沖區的COM引用計數就會向上遞增一次,這便是我們在代碼片段的結尾處釋放它(ReleaseCOM)的原因。
7. 創建深度/模板緩沖區及其視圖
我們現在需要創建深度/模板緩沖區。如4.1.5節所述,深度緩沖區只是一個存儲深度信息的2D紋理(如果使用模板,則模板信息也在該緩沖區中)。要創建紋理,我們必須填充一個D3D11_TEXTURE2D_DESC結構體來描述所要創建的紋理,然后再調用ID3D11Device::CreateTexture2D方法。該結構體的定義如下:
1.Width:紋理的寬度,單位為紋理元素(texel)。
2.Height:紋理的高度,單位為紋理元素(texel)。
3.MipLevels:多級漸近紋理層(mipmap level)的數量。多級漸近紋理將在后面的章節“紋理”中進行講解。對于深度/模板緩沖區來說,我們的紋理只需要一個多級漸近紋理層。
4.ArraySize:在紋理數組中的紋理數量。對于深度/模板緩沖區來說,我們只需要一個紋理。
5.Format:一個DXGI_FORMAT枚舉類型成員,它指定了紋理元素的格式。對于深度/模板緩沖區來說,它必須是4.1.5節列出的格式之一。
6.SampleDesc:多重采樣數量和質量級別;請參閱4.1.7和4.1.8節。
7.Usage:表示紋理用途的D3D11_USAGE枚舉類型成員。有4個可選值:
- D3D11_USAGE_DEFAULT:表示GPU(graphics processing unit,圖形處理器)會對資源執行讀寫操作。CPU不能讀寫這種資源。對于深度/模板緩沖區,我們使用D3D11_USAGE_DEFAULT標志值,因為GPU會執行所有讀寫深度/模板緩沖區的操作。
- D3D10_USAGE_IMMUTABLE:表示在創建資源后,資源中的內容不會改變。這樣可以獲得一些內部優化,因為GPU會以只讀方式訪問這種資源。除了在創建資源時CPU會寫入初始化數據外,其他任何時候CPU都不會對這種資源執行任何讀寫操作。?
- D3D10_USAGE_DYNAMIC:表示應用程序(CPU)會頻繁更新資源中的數據內容(例如,每幀更新一次)。GPU可以從這種資源中讀取數據,而CPU可以向這種資源中寫入數據。
- D3D10_USAGE_STAGING:表示應用程序(CPU)會讀取該資源的一個副本(即,該資源支持從顯存到系統內存的數據復制操作)。
8.BindFlags:指定該資源將會綁定到管線的哪個階段。對于深度/模板緩沖區,該參數應設為D3D11_BIND_DEPTH_STENCIL。其他可用于紋理的綁定標志值還有:
- D3D11_BIND_RENDER_TARGET:將紋理作為一個渲染目標綁定到管線上。
- D3D11_BIND_SHADER_RESOURCE:將紋理作為一個著色器資源綁定到管線上。
9.CPUAccessFlags:指定CPU對資源的訪問權限。如果CPU需要向資源寫入數據,則應指定D3D11_CPU_ACCESS_WRITE。具有寫訪問權限的資源的Usage參數應設為D3D11_USAGE_DYNAMIC或D3D11_USAGE_STAGING。如果CPU需要從資源讀取數據,則應指定D3D11_CPU_ACCESS_READ。具有讀訪問權限的資源的Usage參數應設為D3D11_USAGE_STAGING。對于深度/模板緩沖區來說,只有GPU會執行讀寫操作;所以,我們將該參數設為0,因為CPU不會在深度/模板緩沖區上執行讀寫操作。
10.MiscFlags:可選的標志值,與深度/模板緩沖區無關,所以設為0。
注意:推薦避免使用D3D11_USAGE_DYNAMIC和D3D11_USAGE_STAGING,因為有性能損失。要獲得最佳性能,我們應創建所有的資源并將它們上傳到GPU并保留其上,只有GPU在讀取或寫入這些資源。但是,在某些程序中必須有CPU的參與,因此這些標志無法避免,但你應該將這些標志的使用減到最小。
在本書中,我們會看到以各種不同選項來創建資源的例子;例如,使用不同的Usage標志值、綁定標志值和CPU訪問權限標志值。但就目前來說,我們只需要關心那些與創建深度/模板緩沖區有關的標志值即可,其他選項可以以后再說。
另外,在使用深度/模板緩沖區之前,我們必須為它創建一個綁定到管線上的深度/模板視圖。過程與創建渲染目標視圖的過程相似。下面的代碼示范了如何創建深度/模板紋理以及與它對應的深度/模板視圖:
D3D11_TEXTURE2D_DESC depthStencilDesc; depthStencilDesc.Width = mClientWidth; depthStencilDesc.Height = mClientHeight; depthStencilDesc.MipLevels = 1; depthStencilDesc.ArraySize = 1; depthStencilDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT; // 使用4X MSAA?——必須與交換鏈的MSAA的值匹配 if( mEnable4xMsaa) {depthStencilDesc.SampleDesc.Count = 4;depthStencilDesc.SampleDesc.Quality = m4xMsaaQuality-1; } // 不使用MSAA else {depthStencilDesc.SampleDesc.Count = 1;depthStencilDesc.SampleDesc.Quality = 0; } depthStencilDesc.Usage = D3D10_USAGE_DEFAULT; depthStencilDesc.BindFlags = D3D10_BIND_DEPTH_STENCIL; depthStencilDesc.CPUAccessFlags = 0; depthStencilDesc.MiscFlags = 0; ID3D10Texture2D* mDepthStencilBuffer; ID3D10DepthStencilView* mDepthStencilView;HR(md3dDevice->CreateTexture2D(&depthStencilDesc, 0, &mDepthStencilBuffer));HR(md3dDevice->CreateDepthStencilView(mDepthStencilBuffer, 0, &mDepthStencilView));CreateTexture2D的第二個參數是一個指向初始化數據的指針,這些初始化數據用來填充紋理。不過,由于個紋理被用作深度/模板緩沖區,所以我們不需要為它填充任何初始化數據。當執行深度緩存和模板操作時,Direct3D會自動向深度/模板緩沖區寫入數據。所以,我們在這里將第二個參數指定為空值。
CreateDepthStencilView的第二個參數是一個指向D3D11_DEPTH_STENCIL_VIEW_DESC的指針。這個結構體描述了資源中這個元素數據類型(格式)。如果資源是一個有類型的格式(非typeless),這個參數可以為空值,表示創建一個資源的第一個mipmap等級的視圖(深度/模板緩沖也只能使用一個 mipmap等級)。因為我們指定了深度/模板緩沖的格式,所以將這個參數設置為空值。
8. 將視圖綁定到輸出合并器階段
現在我們已經為后臺緩沖區和深度緩沖區創建了視圖,就可以將些視圖綁定到管線的輸出合并器階段(output merger stage),使些資源成為管線的渲染目標和深度/模板緩沖區:
第一個參數是我們將要綁定的渲染目標的數量;我們在這里僅綁定了一個渲染目標,不過該參數可以為著色器同時綁定多個渲染目標(是一項高級技術)。第二個參數是我們將要綁定的渲染目標視圖數組中的第一個元素的指針。第三個參數是將要綁定到管線的深度/模板視圖。
注意:我們可以設置一組渲染目標視圖,但是只能設置一個深度/模板視圖。使用多個渲染目標是一項高級技術,會在本書的第三部分加以介紹。
9. 設置視口
通常我們會把3D場景渲染到整個后臺緩沖區上。不過,有時我們只希望把3D場景渲染到后臺緩沖區的一個子矩形區域中,如圖4.7所示。
我們將后臺緩沖區的子矩形區域稱為視口(viewport),它由如下結構體描述:
typedef struct D3D11_VIEWPORT {FLOAT TopLeftX;FLOAT TopLeftY;FLOAT Width;FLOAT Height;FLOAT MinDepth;FLOAT MaxDepth; } D3D11_VIEWPORT;前4個數據成員定義了相對于窗口客戶區的視口矩形范圍。MinDepth成員表示深度緩沖區的最小值,MaxDepth表示深度緩沖區的最大值。Direct3D使用的深度緩沖區取值范圍是0到1,除非你想要得到一些特殊效果,否則應將MinDepth和MaxDepth分別設為0和1。在填充了D3D11_VIEWPORT結構體之后,我們可以使用ID3D11Device::RSSetViewports方法設置Direct3D的視口。下面的例子創建和設置了一個視口,該視口與整個后臺緩沖區的大小相同:
D3D11_VIEWPORT vp; vp.TopLeftX = 0; vp.TopLeftY = 0; vp.Width = static_cast<float>(mClientWidth); vp.Height = static_cast<float>(mClientHeight); vp.MinDepth = 0.0f; vp.MaxDepth = 1.0f;md3dImmediateContext-->RSSetViewports(1, &vp);第一個參數是綁定的視圖的數量(可以使用超過1的數量用于高級的效果),第二個參數指向一個viewports的數組。
例如,你可以使用視口來實現雙人游戲模式中的分屏效果。創建兩個視口,各占屏幕的一半,一個居左,另一個居右。然后在左視口中以第一個玩家的視角渲染3D場景,在右視口中以第二個玩家的視角渲染3D場景。你也可以使用視口只繪制到屏幕的一個子矩形中,而在其他區域保留諸如按鈕、列表框之類的UI控件。
10. 程序示例Demo完整項目源代碼下載
http://download.csdn.net/detail/sinat_24229853/9144309
總結
以上是生活随笔為你收集整理的DirectX11 Direct3D初始化的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 物理竞赛应该怎么准备?3个步骤教你敲响名
- 下一篇: sqlserver位数不够左边补0