天天看點

Directx10,11的SwapChain,RenderTarget和DepthBuffer解釋

在開始學Directx10,11時,有幾個常見的概念,或變量如SwapChain,RenderTarget,Depth Buffer都有點似懂非懂,很多時候都是依樣畫葫蘆。寫的多了,加上看了SDK和《Intro to d3d10》後,最近突然煥然大悟。記錄分享下。

1:SwapChain。

SwapChain這個概念還是比較容易懂的,主要負責維護幾個表面(圖像)的集合,當顯示器在展示目前幀時,Directx在背景繪制下一幀。

    怕講的不夠準确清晰,附《Intro to d3d10》的詳細解釋如下:

    To avoid flickering in animation, it is best to draw a frame of animation into an off-screen texture called the back buffer. Once the entire scene has been drawn to the back buffer for the given frame of animation, it is presented to the screen as one complete frame; in this way, the viewer does not watch as the frame gets drawn — the viewer only sees complete frames. The ideal time to present the frame to the screen is during the vertical blanking interval. To implement this, two texture buffers are maintained by the hardware, one called the front buffer and a second called the back buffer. The front buffer stores the image data currently being displayed on the monitor, while the next frame of animation is being drawn to the back buffer. After the frame has been drawn to the back buffer, the roles of the back buffer and front buffer are reversed: The back buffer becomes the front buffer and the front buffer becomes the back buffer for the next frame of animation. Swapping the roles of the back and front buffers is called presenting. Presenting is an efficient operation, as the pointer to the current front buffer and the pointer to the current back buffer just need to be swapped. Figure 4.1 illustrates the process. 

Directx10,11的SwapChain,RenderTarget和DepthBuffer解釋

     Fiture4.1

Front buffer和back buffer合稱SwapChain,在Directx11中它接口是IDXGISwapChain。最常用的函數是IDXGISwapChain::Present(),其将背景繪制的back buffer與前台顯示的front buffer調換。

2:Depth Buffer。

這個也比較容易懂,就是記錄目前所繪制的每個pixel的深度資訊,其資料結構是一個texture。

附錄《Intro to d3d10》的詳細解釋:

The depth buffer is an example of a texture that does not contain image data, but rather depth information about a particular pixel. The possible depth values range from 0.0 to 1.0, where 0.0 denotes the closest an object can be to the viewer and 1.0 denotes the farthest an object can be from the viewer. There is a one-to-one correspondence between each element in the depth buffer and each pixel in the back buffer (i.e., the ijth element in the back buffer corresponds to the ijth element in the depth buffer). So if the back buffer had a resolution of 1280×1024, there would be 1280×1024 depth entries.

還有個和DepthBuffer對應的Stencil buffer。暫時還沒用過它,隻好跳過先。下面可能有時說DepthBuffer,有時說Depth/Stencil Buffer。不過主要指的都是Depth Buffer。

明白了SwapChain和DepthBuffer的概念後,下面分别說下在程式中具體如何使用SwapChain和DepthBuffer。

對于SwapChain,一般來說主要在三個地方用到。

第一:初始化并建立IDXGISwapChain接口。我們一般在一開始聲明一個全局的IDXGISwapChain *pSwapChain。然後建立個DXGI_SWAP_CHAIN_DESC結構來描述我們自己的SwapChain的一些細節。最後再調用函數D3D11CreateDeviceAndSwapChain()來建立IDXGISwapChain。

程式的具體實作如下:

    DXGI_SWAP_CHAIN_DESC scDesc;

    ZeroMemory(&scDesc,sizeof(DXGI_SWAP_CHAIN_DESC));

    scDesc.BufferCount=1;

    scDesc.BufferDesc.Height=height;

    scDesc.BufferDesc.Width=width;

    scDesc.BufferDesc.Format=DXGI_FORMAT_R8G8B8A8_UNORM;

    scDesc.BufferDesc.RefreshRate.Numerator=60;

    scDesc.BufferDesc.RefreshRate.Denominator=1;

    scDesc.BufferUsage=DXGI_USAGE_RENDER_TARGET_OUTPUT;

    scDesc.OutputWindow=m_hWnd;

    scDesc.SampleDesc.Count=1;

    scDesc.SampleDesc.Quality=0;

    scDesc.Windowed=TRUE;

  hr=D3D11CreateDeviceAndSwapChain(NULL,driverTypes,NULL,createDeviceFlags,featureLevels,numFeatureLevels,D3D11_SDK_VERSION,&scDesc,&m_pSwapChain,&m_pDevice,&featureLevelOut,&m_pContext);

第二:建立Render Target View。這個讓我有點迷糊了一段時間。寫了段時間d3d11程式,加上看sdk和《intro to d3d10》後清晰了不少。

首先為什麼要建立這麼一個render target view呢?首先從render target說起,什麼是render target呢?前面我們說了,在顯示front buffer的時候,Directx在背景渲染back buffer,這就是render target。

為了讓Directx能夠渲染這個render target,必須将這個buffer綁定到Directx的繪制管道(pipeline)。但是在Directx11中我們不能直接将resource綁定到繪制管道(pipeline),作為更好的方法,我們建立該resource view,然後将它綁定到渲染管道,這樣我們就能繪制這個resource了。是以這就是RenderTargetView的由來,以及為什麼我們要建立它。

具體建立方法如下:

ID3D11RenderTargetView *pRTV;

ID3D11Texture2D *pBackBuffer;

pSwapChain->GetBuffer(0,__uuidof(ID3D11Texture2D),(void**)(&backBuffer)); 

pDevice->CreateRenderTargetView(backBuffer,0,&pRTV);

ReleaseCOM(backBuffer);

第三:使用swapChain。1:清空RenderTarget,主要是清空上一幀在render target中繪制的東西,以開始重新繪制新的一幀内容。

 在程式中具體使用方法,在每幀調用的繪制函數DrawFunction()的開始加上一句pSwapChain->CleaerRendrTargetView()就可以了。如下

void DrawFunction(……)

{

        float clearColor[4]={0.5f,0.5f,0.5f,0.0f};

m_pContext->ClearRenderTargetView(m_pRTV,clearColor);

/

……

/

}

2:把背景繪制好的back buffer與前台的front buffer調換。這個通過IDXGISwapChain::Present()來完成。這個就解釋了我曾經的疑惑,因為我覺得把一開始的back buffer設定為render target,但是back buffer和front buffer是在不斷交換的啊,也就是render target是不斷地變換的啊。這就要通過函數Present()來協調了。每繪制好back buffer,并把它與前台的front buffer調換的時候,render target也就自動指向了調回來的front buffer(已經是back buffer了)。

 在程式中具體使用方法,在每幀調用的繪制函數DrawFunction()的最後加上一句pSwapChain->Present(0,0)就可以了。如下

void DrawFunction(……)

{

        float clearColor[4]={0.5f,0.5f,0.5f,0.0f};

m_pContext->ClearRenderTargetView(m_pRTV,clearColor);

/

……

/

pSwapChain->Present(0,0);

}

在明白了SwapChain和RenderTarget and view用法後,我們再來看看如何在程式中如何建立和使用Depth/Stencil buffer and view。

第一:建立Depth/Stencil buffer and view。因為前面說了,Depth/Stencil buffer其實就是一個texture。一開始為空,後面根據場景,全部由GPU幫我們自動計算讀寫。是以建立Depth/Stencil buffer and view就和建立一個texture resource和相應resource view一樣。先聲明一個接口ID3D11Texture2D* m_pBackBuffer;然後建立一個描述該texture的結構D3D11_TEXTURE2D_DESC;最後通過ID3D11Device::CreateTexture2D()來建立這個DepthBuffer。對于DepthBuffer view,我們隻需再調用ID3D11Device::CreateDepthStencilView()來建立該DepthBuffer的view。

程式的具體實作如下:

//create depth stencil buffer.

    D3D11_TEXTURE2D_DESC depthDesc;

    ZeroMemory(&depthDesc,sizeof(D3D11_TEXTURE2D_DESC));

    depthDesc.ArraySize=1;

    depthDesc.BindFlags=D3D11_BIND_DEPTH_STENCIL;

    depthDesc.CPUAccessFlags=0;

    depthDesc.Format=DXGI_FORMAT_D24_UNORM_S8_UINT;

    depthDesc.Height=height;

    depthDesc.Width=width;

    depthDesc.MipLevels=1;

    depthDesc.MiscFlags=0;

    depthDesc.SampleDesc.Count=1;

    depthDesc.SampleDesc.Quality=0;

    depthDesc.Usage=D3D11_USAGE_DEFAULT;

    hr=m_pDevice->CreateTexture2D(&depthDesc,NULL,&m_pDepthStencil);

    //create the depth stencil buffer view.

    D3D11_DEPTH_STENCIL_VIEW_DESC dsViewDesc;

    ZeroMemory(&dsViewDesc,sizeof(D3D11_DEPTH_STENCIL_VIEW_DESC));

    dsViewDesc.Format=depthDesc.Format;

    dsViewDesc.ViewDimension=D3D11_DSV_DIMENSION_TEXTURE2D;

    dsViewDesc.Texture2D.MipSlice=0;

    hr=m_pDevice->CreateDepthStencilView(m_pDepthStencil,&dsViewDesc,&m_pDepthStencilView);

第二:使用Detph/Stencil buffer。主要在每幀的繪制函數DrawFunction()開始調用ID3D11DeviceContext::ClearDepthStencilView()來清空Depth/Stencil Buffer,後面的計算GPU會自動替我們做。

void DrawFunction(……)

{

       float clearColor[4]={0.5f,0.5f,0.5f,0.0f};

m_pContext->ClearRenderTargetView(m_pRTV,clearColor);

m_pContext->ClearDepthStencilView(m_pDepthStencilView,D3D11_CLEAR_DEPTH,1.0f,0.0f);

/

……

/

pSwapChain->Present(0,0);

}

最後當我們建立好了render taget view和depth buffer view,我們就将其綁定到pipeline的output merget stage,使相應的resource正式成為pipeline的render target和depth buffer。

m_pDevice->OMSetRenderTarget(1,&m_pRTV,m_pDepthStencilView);

繼續閱讀