1. 程式人生 > >Direct3D 11入門級知識介紹

Direct3D 11入門級知識介紹

               

本系列文章由zhmxy555(毛星雲)編寫,轉載請註明出處。

作者:毛星雲    郵箱: [email protected]    期待著與志同道合的朋友們相互交流

上一節裡我們介紹了在邁入DirectX 11的學習旅程之後第一個demo建立的全過程。但由於知識銜接的需要,我們的第一個demo裡面涉及到的大部分知識都是關於Win32的。而為了使之前講解的Blank Win32 Window Demo蛻變成我們期望的Direct3D的模樣,我們將在這節的筆記裡面對Direct3D的入門級的基礎知識做一個詳細的介紹,以便在下節筆記裡輕車熟路地寫出屬於我們的第一個完整的Direct3D11 Demo。

       由於DirectX 11龐雜的知識體系,初學DirectX11在所難免地會面對浩如煙海而略顯枯燥的基礎知識的介紹,在依次介紹完必要的知識之後,淺墨會按照【Visual C++】遊戲開發筆記系列專欄的傳統風格,逐步放出幾個有趣的基於DirectX 11的小遊戲demo,敬請期待。

入門知識的第一步當然是進行DirectX開發環境的配置,這在筆記二十五里面有詳細介紹,詳情請移步:

下面就開始正題,我們將分八個部分對入門級的Direct3D知識進行一個講解。

一、 Direct3D的初始化

初始化Direct3D,我們需要完成以下四個步驟:

1.定義我們需要檢查的裝置型別(device types)和特徵級別(feature levels)

2.建立Direct3D裝置,渲染裝置(context)和交換鏈(swap chain)。

3.建立渲染目標(render target)。

4.設定視口(viewport)

這裡只是給大家一個框架的概念,各個部分下面會詳細展開講解。

二、驅動裝置型別與特徵等級

在Direct3D 11中我們能使用的裝置有硬體裝置(hardware device),參考裝置(reference device),軟體驅動裝置(software driver device), 以及WARP裝置 (WARP device)。

硬體裝置(hardware device)是一個執行在顯示卡上的D3D裝置,在所有裝置中執行速度是最快的。這將是我們日後討論最多的一種型別。

參考裝置(reference device)是用於沒有可用的硬體支援時在CPU上進行渲染的裝置。

簡言之,參考裝置就是利用軟體,在CPU對硬體渲染裝置的一個模擬。但是不幸的是,這種方式非常的低效,所以在開發過程中,沒有其他可用選擇的時候,我們才採用這種方式。比如新一代的DirectX釋出了,市面上還沒有支援這種新版本DirectX的硬體,我們在開發過程中就只能採用這種方式來跑了。

軟體驅動裝置(software driverdevice)是開發人員自己編寫的用於Direct3D的渲染驅動軟體。這種方式通常不推薦用於高效能或者對效能要求苛刻的應用程式,下面介紹的WARP裝置將是更好的選擇。

WARP裝置(WARPdevice)是一種高效的CPU渲染裝置,可以模擬現階段所有的Direct3D特性。WARP使用了Windows Vista /Windows 7/Winodws 8中的Windows Graphic 執行庫中高度優化過的程式碼作為支撐,這讓這種方式出類拔萃,相比與上文提到的參考裝置(reference device)模式更加優秀。WARP裝置在配置不高的機器上面可以達到化腐朽為神奇的功效。在我們的硬體不支援實時應用程式(real-time application)的情況下,用WARP裝置作為替補是一個明智的選擇,因為相比而言,參考裝置(reference device)的執行效率實在是無法令人恭維。即便如此,WARP裝置的執行效率還是不能和硬體裝置同日而語,畢竟它依舊是對硬體的一種模擬,即使這種模擬是非常高效的。

注意:這不是對裝置型別一個完整的列舉,還有很多細枝末節的裝置型別,在這裡沒必要一一列舉

Direct3D的特徵等級用於指定需要設定的裝置目標。在這個專欄之中,我們將針對三種裝置,第一種當然是我們的Direct3D 11裝置,第二種為Direct3D 10.1裝置,第三種為Direct3D 10.0裝置。再這三種裝置都無法支援的情況下,我們再選擇WARP裝置或者參考裝置作為後援。

下面貼出來的程式碼段1為後面我們需要用到的驅動型別和特徵級別的一個宣告。通過建立各種型別的陣列,我們可以使用迴圈來嘗試首先建立我們最需要的裝置,然後若執行失敗則繼續建立其他的裝置型別。淺墨記得我們之前提到過,Win32巨集ARRAYSIZE能夠用來返回一個數組的大小,Win32函式GetClientRect可以用來計算應用程式客戶區的大小。算出來的值會用於設定之後的D3D裝置渲染的寬度和高度。

另外,需要記住Win32應用程式是分客戶區和非客戶區的,我們僅能在客戶區上進行渲染。

程式碼段1 指明驅動裝置型別和特徵等級

[cpp] view plaincopyprint?
  1. RECT dimensions;  
  2. GetClientRect( hwnd, &dimensions );  
  3. unsigned int width = dimensions.right - dimensions.left;  
  4. unsigned int height = dimensions.bottom - dimensions.top;  
  5. D3D_DRIVER_TYPE driverTypes[] =  
  6. {  
  7. D3D_DRIVER_TYPE_HARDWARE, D3D_DRIVER_TYPE_WARP,D3D_DRIVER_TYPE_SOFTWARE  
  8. };  
  9. unsigned int totalDriverTypes = ARRAYSIZE( driverTypes );  
  10. D3D_FEATURE_LEVEL featureLevels[] =  
  11. {  
  12. D3D_FEATURE_LEVEL_11_0,  
  13. D3D_FEATURE_LEVEL_10_1,  
  14. D3D_FEATURE_LEVEL_10_0  
  15. };  
  16. unsigned int totalFeatureLevels = ARRAYSIZE( featureLevels );  
RECT dimensions;GetClientRect( hwnd, &dimensions );unsigned int width = dimensions.right - dimensions.left;unsigned int height = dimensions.bottom - dimensions.top;D3D_DRIVER_TYPE driverTypes[] ={D3D_DRIVER_TYPE_HARDWARE, D3D_DRIVER_TYPE_WARP,D3D_DRIVER_TYPE_SOFTWARE};unsigned int totalDriverTypes = ARRAYSIZE( driverTypes );D3D_FEATURE_LEVEL featureLevels[] ={D3D_FEATURE_LEVEL_11_0,D3D_FEATURE_LEVEL_10_1,D3D_FEATURE_LEVEL_10_0};unsigned int totalFeatureLevels = ARRAYSIZE( featureLevels );

三、裝置與交換鏈的建立

下一步便是建立一個交換鏈,交換鏈在Direct3D中為一個裝置渲染目標的集合。每一個裝置都有至少一個交換鏈,而多個交換鏈能夠被多個裝置所建立。一個交換目標可以為一個渲染和顯示到螢幕上的顏色快取(在後面會討論),等等。

通常在遊戲中有,有兩種顏色快取,分別叫做主快取和輔助快取,他們一起被稱為前後臺快取組合。主快取中的內容(前臺快取)會顯示在螢幕上,而輔助快取(後臺快取)用於繪製下一幀(真是兩個好基友-o-)。

渲染的發生非常之快,螢幕的一部分可以在顯示器完成顯示更新之前,在先前的結果為基礎上進行繪製。快取之間的切換,可以進行一個良性的運作,前臺在顯示影象,後臺正在為前臺準備下一刻將要顯示的影象,這樣做可以避免很多棘手的問題,提高了效率。

這種技術在計算機圖形學中叫做雙緩衝(doublebuffering),或者叫頁面翻轉(page flipping)(這種技術我們之前的一系列Win32 GDI demo中使用得比較勤,研究了之前的demo的朋友們應該已經耳濡目染了吧)。一個交換鏈能擁有一個或者多個這樣的緩衝。

程式碼段2中列出了建立一個交換鏈的程式碼。一個交換鏈的描述用來定義和建立符合我們需要的交換鏈。

程式碼段2 對交換鏈的設定

[cpp] view plaincopyprint?
  1. DXGI_SWAP_CHAIN_DESC swapChainDesc;  
  2. ZeroMemory( &swapChainDesc, sizeof( swapChainDesc ) );  
  3. swapChainDesc.BufferCount = 1;  
  4. swapChainDesc.BufferDesc.Width = width;  
  5. swapChainDesc.BufferDesc.Height = height;  
  6. swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;  
  7. swapChainDesc.BufferDesc.RefreshRate.Numerator = 60;  
  8. swapChainDesc.BufferDesc.RefreshRate.Denominator = 1;  
  9. swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;  
  10. swapChainDesc.OutputWindow = hwnd;  
  11. swapChainDesc.Windowed = true;  
  12. swapChainDesc.SampleDesc.Count = 1;  
  13. swapChainDesc.SampleDesc.Quality = 0;  
DXGI_SWAP_CHAIN_DESC swapChainDesc;ZeroMemory( &swapChainDesc, sizeof( swapChainDesc ) );swapChainDesc.BufferCount = 1;swapChainDesc.BufferDesc.Width = width;swapChainDesc.BufferDesc.Height = height;swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;swapChainDesc.BufferDesc.RefreshRate.Numerator = 60;swapChainDesc.BufferDesc.RefreshRate.Denominator = 1;swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;swapChainDesc.OutputWindow = hwnd;swapChainDesc.Windowed = true;swapChainDesc.SampleDesc.Count = 1;swapChainDesc.SampleDesc.Quality = 0

這個範例中定義了D3D的多種取樣屬性,多重取樣(Multisampling)是一種用於取樣和平衡渲染畫素的建立亮麗色彩變化之間的平滑過渡的一種技術。

快取的使用和交換鏈的描述有大量的成員需要設定,但這些設定都是非常簡單的。快取的對交換鏈的使用是設定下DXGI_USAGE_RENDER_TARGET_OUTPUT,以便交換鏈能夠用於輸出,或者換句話說,它能被渲染。

下一步是建立渲染上下文,渲染裝置,以及我們擁有的交換鏈描述。D3D裝置一般都是裝置本身和硬體之間的通訊,而D3D上下文是一種描述裝置如何繪製的渲染裝置上下文,這也包含了渲染狀態和其他的繪圖資訊。

正如我們討論過的,交換鏈是裝置和上下文將要繪製的渲染目標。

建立裝置上下文,渲染上下文和交換鏈所需的程式碼在程式碼段3中詳細列出了,.這段程式碼為下次內容即將展示的Direct3D 11 BlankWindows Demo的一個片段。

程式碼段3 Direct3D裝置,裝置上下文,以及交換鏈的建立

[cpp] view plaincopyprint?
  1. ID3D11Device device_;  
  2. ID3D11Context d3dContext_;  
  3. IDXGISwapChain swapChain_;  
  4. unsigned int creationFlags = 0;  
  5. #ifdef _DEBUG
  6. creationFlags |= D3D11_CREATE_DEVICE_DEBUG;  
  7. #endif
  8. HRESULT result;  
  9. unsigned int driver = 0;  
  10. for( driver = 0; driver < totalDriverTypes; ++driver )  
  11. {  
  12. result = D3D11CreateDeviceAndSwapChain( 0, driverTypes[driver],0,  
  13. creationFlags, featureLevels, totalFeatureLevels,  
  14. D3D11_SDK_VERSION, &swapChainDesc, &swapChain_,  
  15. &d3dDevice_, &featureLevel_, &d3dContext_ );  
  16. if( SUCCEEDED( result ) )  
  17. {  
  18. driverType_ = driverTypes[driver];  
  19. break;  
  20. }  
  21. }  
  22. if( FAILED( result ) )  
  23. {  
  24. DXTRACE_MSG( "Failed to create the Direct3D device!");  
  25. returnfalse;  
  26. }  
ID3D11Device device_;ID3D11Context d3dContext_;IDXGISwapChain swapChain_;unsigned int creationFlags = 0;#ifdef _DEBUGcreationFlags |= D3D11_CREATE_DEVICE_DEBUG;#endifHRESULT result;unsigned int driver = 0;for( driver = 0; driver < totalDriverTypes; ++driver ){result = D3D11CreateDeviceAndSwapChain( 0, driverTypes[driver],0,creationFlags, featureLevels, totalFeatureLevels,D3D11_SDK_VERSION, &swapChainDesc, &swapChain_,&d3dDevice_, &featureLevel_, &d3dContext_ );if( SUCCEEDED( result ) ){driverType_ = driverTypes[driver];break;}}if( FAILED( result ) ){DXTRACE_MSG( "Failed to create the Direct3D device!");return false;} 

交換鏈,裝置和渲染上下文可以在單獨的Direct3D函式呼叫中被建立,或者通過特定物件的Direct3D來呼叫(例如用CreateSwapChain函式來專門建立一個交換鏈)。

這個函式為D3D11CreateDeviceAndSwapChain。在程式碼段2中我們在每個驅動型別中迴圈,試圖建立一個合適得裝置,或為一個硬體裝置,或為一個WARP裝置,抑或一個參考裝置(reference device)。因為如果建立失敗,我們就無法初始化我們的Direct3D。

D3D11CreateDeviceAndSwapChain函式中包含了特徵等級作為其引數, 所以如果至少有一個這樣的特徵等級存在,而且若我們的裝置型別也存在,這個函式才會執行成功。

其中D3D11CreateDeviceAndSwapChain函式具有如下的函式原型:

[cpp] view plaincopyprint?
  1. HRESULT D3D11CreateDeviceAndSwapChain(  
  2.    IDXGIAdapter *pAdapter,  
  3.    D3D_DRIVER_TYPEDriverType,  
  4. HMODULE Software,  
  5. UINT Flags,  
  6. const D3D_FEATURE_LEVEL*pFeatureLevels,  
  7. UINT FeatureLevels,  
  8. UINT SDKVersion,  
  9. const DXGI_SWAP_CHAIN_DESC *pSwapChainDesc,  
  10.    IDXGISwapChain **ppSwapChain,  
  11.    ID3D11Device **ppDevice,  
  12.    D3D_FEATURE_LEVEL*pFeatureLevel,  
  13.    ID3D11DeviceContext**ppImmediateContext  
  14. );  
HRESULT D3D11CreateDeviceAndSwapChain(   IDXGIAdapter *pAdapter,   D3D_DRIVER_TYPEDriverType,   HMODULE Software,   UINT Flags,   const D3D_FEATURE_LEVEL*pFeatureLevels,   UINT FeatureLevels,   UINT SDKVersion,   const DXGI_SWAP_CHAIN_DESC *pSwapChainDesc,   IDXGISwapChain **ppSwapChain,   ID3D11Device **ppDevice,   D3D_FEATURE_LEVEL*pFeatureLevel,   ID3D11DeviceContext**ppImmediateContext);

     四、 建立渲染目標檢視

一個渲染目標檢視是一個由Output MergerStage讀取的D3D資源。為了output merger能渲染一個後臺快取的交換鏈,我們為其建立一個渲染目標檢視。

由於紋理的概念說來話長,目前我們將紋理理解為一副影象就行了,後面中我們將展開討論紋理的很多細節內容,。交換鏈的主快取和輔助快取為彩色的影象,為了獲得它們的指標,我們一般會呼叫交換鏈中的函式GetBuffer。

得到指向快取的指標後,我們呼叫Direct3D中的函式CreateRenderTargetView,來建立一個渲染目標檢視(rendertarget view.)。渲染目標檢視含有ID3D11RenderTargetView型別,而CreateRenderTargetView函式將建立我們檢視的2D紋理,渲染目標描述,我們建立的ID3D11RenderTargetView的物件地址為其函式變數。將渲染目標描述變數設為空給我們所有的表面的MIP對映水平都為0級,MIP對映水平也將在後面進行詳細討論。

我們完成渲染目標的建立之後,就能夠釋放指標到交換鏈的後臺快取了。因為得到了COM物件的一個引用,我們必須呼叫COM中的Release函式來減少引用的數量。這樣做會避免記憶體的洩露,因為我們不想應用程式退出後,系統仍然保留著這裡記憶體,這將導致系統資源的浪費,而這種浪費是不科學的。

在每次我們想渲染一個特定的渲染目標的時候,必須在所有的繪製的函式呼叫之前對它進行設定。這個重任就交給了我們的OMSetRenderTarget函式,這個函式隸屬於output merger,在之後會講到。

程式碼段4 渲染目標檢視的建立和繫結

[cpp] view plaincopyprint?
  1. ID3D11RenderTargetView* backBufferTarget_;  
  2. ID3D11Texture2D* backBufferTexture;  
  3. HRESULT result = swapChain_->GetBuffer( 0, __uuidof(ID3D11Texture2D ),  
  4. LPVOID* )&backBufferTexture );  
  5. if( FAILED( result ) )  
  6. {  
  7. DXTRACE_MSG( "Failed to get the swap chain backbuffer!" );  
  8. returnfalse;  
  9. }  
  10. result = d3dDevice_->CreateRenderTargetView(backBufferTexture, 0,  
  11. &backBufferTarget_ );  
  12. if( backBufferTexture )  
  13. backBufferTexture->Release( );  
  14. if( FAILED( result ) )  
  15. {  
  16. DXTRACE_MSG( "Failed to create the render targetview!" );  
  17. returnfalse;  
  18. }  
  19. d3dContext_->OMSetRenderTargets( 1, &backBufferTarget_, 0);  
ID3D11RenderTargetView* backBufferTarget_;ID3D11Texture2D* backBufferTexture;HRESULT result = swapChain_->GetBuffer( 0, __uuidof(ID3D11Texture2D ),( LPVOID* )&backBufferTexture );if( FAILED( result ) ){DXTRACE_MSG( "Failed to get the swap chain backbuffer!" );return false;}result = d3dDevice_->CreateRenderTargetView(backBufferTexture, 0,&backBufferTarget_ );if( backBufferTexture )backBufferTexture->Release( );if( FAILED( result ) ){DXTRACE_MSG( "Failed to create the render targetview!" );return false;}d3dContext_->OMSetRenderTargets( 1, &backBufferTarget_, 0);

在程式碼段4中你會注意到我們採用了一個叫做DXTRACE_MSG的巨集。這個巨集用作debugging來用。在之後將進行更詳細的講解。

       五、 視口

Direct3D中 的一個重點同時也是難點在於建立和設定視口。

視口定義了我們渲染到螢幕上的面積。在單人或者非分割畫面的多人遊戲中一般都為全屏,所以我們設定視口的寬度和高度即為交換鏈的寬度和高度。對於分屏遊戲,我們可以建立兩個視口,一個視口定義在螢幕上方,另一個定義在螢幕下方。為了渲染分屏視口,我們可以分別以兩位不同玩家的角度來渲染。

視點的建立由填充D3D11_VIEWPORT函式和設定呼叫上下文的RSSetViewports函式將其設定到渲染上下文中來完成。RSSetViewports函式需要我們設定的視口數量和視口物件的列舉。全屏視口的建立和設定的相關程式碼在程式碼段五中有列舉,其中X和Y標明左側和頂部螢幕的位置,最小和最大深度是0到1之間的值,表明了視口深度的最小和最大值。

程式碼段5  全屏視口的建立和設定

[cpp] view plaincopyprint?
  1. D3D11_VIEWPORT viewport;  
  2. viewport.Width = static_cast<float>(width);  
  3. viewport.Height = static_cast<float>(height);  
  4. viewport.MinDepth = 0.0f;  
  5. viewport.MaxDepth = 1.0f;  
  6. viewport.TopLeftX = 0.0f;  
  7. viewport.TopLeftY = 0.0f;  
  8. d3dContext_->RSSetViewports( 1, &viewport );  
D3D11_VIEWPORT viewport;viewport.Width = static_cast<float>(width);viewport.Height = static_cast<float>(height);viewport.MinDepth = 0.0f;viewport.MaxDepth = 1.0f;viewport.TopLeftX = 0.0f;viewport.TopLeftY = 0.0f;d3dContext_->RSSetViewports( 1, &viewport );

     六、清除與顯示螢幕

渲染到螢幕需要幾個不同的步驟。第一步通常是清除相關渲染目標的表面。在大部分遊戲中這一步包含了深度快取等一系列內容。在下一節即將呈現的demo中我們將在本章稍後實施,我們將清除渲染目標檢視的顏色緩衝區到一種特定的顏色。這由呼叫D3D中的ClearRenderTargetView函式來完成。ClearRenderTargetView擁有如下的函式原型:

[cpp] view plaincopyprint?
  1. void ClearRenderTargetView( ID3D11RenderTargetView*pRenderTargetView,  
  2. constFLOAT ColorRGBA[4] );  
void ClearRenderTargetView( ID3D11RenderTargetView*pRenderTargetView,const FLOAT ColorRGBA[4] );

注:目前的大部分商業遊戲中在渲染之前清除顏色快取並不是必須的,因為像天空這樣的環境圖形要確保每個畫素都會被顏色快取所覆蓋著。

ClearRenderTargetView函式以將被清理的渲染目標檢視作為其變數。為了清除螢幕,我們設定某種顏色作為我們需要的背景陰影的顏色。這種顏色可以是紅色,綠色,藍色,和透明色Alpha陣列中任意指定的0.0到1.0之間的顏色。這裡0.0表示強度為0,而1.0表示完全飽滿的強度。若對應於位元組,1.0對應255。如果為紅綠藍顏色組合都為1.0,則會得到純白的顏色。下一步就是繪製場景的幾何形狀了,最後一步是呼叫交換鏈的Present函式在螢幕上顯示渲染緩衝區的內容。

Present函式具有以下的宣告:

[cpp] view plaincopyprint?
  1. HRESULT IDXGISwapChain::Present( UINT SyncInterval, UINT Flags);  
HRESULT IDXGISwapChain::Present( UINT SyncInterval, UINT Flags);

對Present函式的引數一個簡單的理解:syncinterval 同步間隔,

    flags 演示的標誌。

在第n個垂直空白之後,Syncinterval能被設定為0,1,2,3,4來顯示。垂直空白是當前幀的最後一列更新時間與下一幀的第一列更新時間的時間差。像電腦顯示器這樣的裝置顯示更新畫素為垂直的,一列一列進行更新的。

Present函式的flags值可被設為0,表示輸出到每一個緩衝區,設為DXGI_PRESENT_ TEST時則表示測試時不進行輸出,或為DXGI_PRESENT_DO_ NOT_SEQUENCE表示不進行排序地利用垂直空白同步輸出來顯示輸出。為達到預期的目的,我們可以只是傳遞0到Present函式來顯示我們的渲染結果。

程式碼段五 展示了一個清屏和顯示檢視的例子。在後面我們將深入探究顏色快取,深度存,使畫面流暢無比的雙緩衝等等。

程式碼段6  清除渲染目標然後顯示顯得渲染場景

[cpp] view plaincopyprint?
  1. float clearColor[4] = { 0.0f, 0.0f, 0.25f, 1.0f };  
  2. d3dContext_->ClearRenderTargetView( backBufferTarget_,clearColor );  
  3. swapChain_->Present( 0, 0 );  
float clearColor[4] = { 0.0f, 0.0f, 0.25f, 1.0f };d3dContext_->ClearRenderTargetView( backBufferTarget_,clearColor );swapChain_->Present( 0, 0 ); 

七、關於格式

有時候我們需要建立指定的DXGI格式。格式可以用於描述一張影象的佈局,每種顏色的位數,或者頂點快取的佈局(後面會講到)。大多數情況下,DXGI格式用於描述交換鏈中的頂點佈局。

舉個例子,DXGI_FORMAT_R8G8B8A8_UNORM,它表示我們定義的每一個RGBA組成部分的資料都為8位。

沒指名型別的格式我們稱作無型別格式(typeless formats)。他們為每個部分儲存相同的位數,但是並不注重包含了什麼型別的資料。如DXGI_FORMAT_R32G32B32A32_TYPELESS。常用的清單型別在下面中列出了。

常用的資料格式型別清單:

DXGI_FORMAT_R32G32B32A32_TYPELESS    128位RGBA無型別格式

DXGI_FORMAT_R32G32B32A32_FLOAT  128位RGBA浮點型格式

DXGI_FORMAT_R32G32B32A32_UINT   128位RGBA無符號整型格式

DXGI_FORMAT_R32G32B32A32_SINT   128位RGBA帶符號整型格式

DXGI_FORMAT_R8G8B8A8_TYPELESS  32位RGBA無型別格式

DXGI_FORMAT_R8G8B8A8_UINT    32位RGBA無符號整型格式

DXGI_FORMAT_R8G8B8A8_SINT    32位RGBA帶符號整型格式

當定義頂點格式的時候,比如DXGI_FORMAT_R32G32B32_FLOAT格式,就是說RGB值都支援是32位的資料型別。有時候,我們會看到特殊的為每一部分指定相同位數的格式,但是他們有不同的副檔名。

舉個例子,DXGI_FORMAT_R32G32B32A32_FLOAT 和DXGI_FORMAT_R32G32B32A32_UINT型別的各個部分的位數都是相同的,不同的各個位數上一個是32位的浮點型,一個是32位的無符號整型。

    八、 善後工作

       Direct3D應用程式中要做的最後一件事情,就是清除和釋放我們建立的物件。舉個例子,在應用程式開頭,我們要建立一個D3D的裝置,一個D3D的渲染上下文,一個交換鏈,以及一個要渲染的目標。當這個應用程式關閉的時候,我們需要釋放這些物件,以將這些資源返還給系統。

COM物件保持一個引用計數,告知系統什麼時候從記憶體中移除這些物件是安全的。通過運用Release函式,我們減少了一個物件的引用數量。當引用數量達到0,系統便會回收這些資源。

下面是一個釋放D3D物件的範例。用首先用if條件句來確保物件不為null,然後呼叫Release函式。通常我們以和建立時相反的順序來釋放這些物件。

程式碼段7  釋放Direct3D 11 main物件

[cpp] view plaincopyprint?
  1. if( backBufferTarget_ )backBufferTarget_->Release( );  
  2. if( swapChain_ ) swapChain_->Release( );  
  3. if( d3dContext_ ) d3dContext_->Release( );  
  4. if( d3dDevice_ ) d3dDevice_->Release( );  
if( backBufferTarget_ )backBufferTarget_->Release( );if( swapChain_ ) swapChain_->Release( );if( d3dContext_ ) d3dContext_->Release( );if( d3dDevice_ ) d3dDevice_->Release( ); 

心得:在釋放物件前,我們經常通過檢查來確保DirectX物件不為null。因為試圖釋放一個非法的指標是非常不科學的,這會使我們遊戲程式的穩定性蕩然無存,經常各種無故崩潰。

本篇文章到這裡就結束了,謝謝欣賞。

感謝一直支援【Visual C++】遊戲開發筆記系列專欄的朋友們。

【Visual C++】遊戲開發 系列文章才剛剛展開一點而已,因為遊戲世界實在是太博大精深了~

但我們不能著急,得慢慢打好基礎。做學問最忌好高騖遠,不是嗎?

淺墨希望看到大家的留言,希望與大家共同交流,希望得到睿智的評論(即使是批評)。

你們的支援是我寫下去的動力~

精通遊戲開發的路還很長很長,非常希望能和大家一起交流,共同學習,共同進步。

大家看過後覺得值得一看的話,可以頂一下這篇文章,你們的支援是我繼續寫下去的動力~

如果文章中有什麼疏漏的地方,也請大家指正。也希望大家可以多留言來和我探討相關的問題。

最後,謝謝你們一直的支援~~~

                                                 ——————————淺墨於2012年7月1日