1. 程式人生 > >基於 DirectX11 的 MMDViewer 04-渲染目標視圖和多視口

基於 DirectX11 的 MMDViewer 04-渲染目標視圖和多視口

ice element play strip() ifdef https warp output for

  這篇文章主要介紹渲染管線輸出部分的內容:交換鏈和渲染目標對象,並且介紹多視口渲染方法。

  交換鏈:


  要創建交換鏈,必須先設置交換鏈描述。交換鏈描述定義了將由交換鏈使用的渲染緩沖區的大小和數量。它還將窗口與交換鏈相關聯,從而確定最終圖像的顯示位置。交換鏈描述還定義了該應用的消除鋸齒(如果有的話)的質量以及在展示過程中後端緩沖區的翻轉方式。

    UINT create_device_flags = 0;
#ifdef _DEBUG
    create_device_flags |= D3D11_CREATE_DEVICE_DEBUG;
#endif

    D3D_DRIVER_TYPE driver_types[] 
= { D3D_DRIVER_TYPE_HARDWARE, D3D_DRIVER_TYPE_WARP, D3D_DRIVER_TYPE_REFERENCE, }; UINT num_driver_types = ARRAYSIZE(driver_types); D3D_FEATURE_LEVEL featureLevels[] = { D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0, }; UINT numFeatureLevels
= ARRAYSIZE(featureLevels); DXGI_SWAP_CHAIN_DESC swap_desc; ZeroMemory(&swap_desc, sizeof(swap_desc)); swap_desc.BufferCount = 1; swap_desc.BufferDesc.Width = width; swap_desc.BufferDesc.Height = height; swap_desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; swap_desc.BufferDesc.RefreshRate.Numerator
= 60; swap_desc.BufferDesc.RefreshRate.Denominator = 1; swap_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; swap_desc.OutputWindow = g_hWnd; swap_desc.SampleDesc.Count = 1; swap_desc.SampleDesc.Quality = 0; swap_desc.Windowed = TRUE; for ( UINT driver_type_index = 0; driver_type_index < num_driver_types; driver_type_index++ ) { g_driverType = driver_types[driver_type_index]; hr = D3D11CreateDeviceAndSwapChain(NULL, g_driverType, NULL, create_device_flags, featureLevels, numFeatureLevels, D3D11_SDK_VERSION, &swap_desc, &g_pSwapChain, &g_pd3dDevice, &g_featureLevel, &g_pImmediateContext); if ( SUCCEEDED(hr) ) break; }

  創建交換鏈是指定了一個窗口的句柄,並且設置了一個 D3D11_CREATE_DEVICE_DEBUG 標誌,這個標誌可以創建一個支持調試層的設備。如果我們做錯了事,調試層為渲染管線的正確性和一致性提供了額外的檢查並提供了更好的反饋。但是,如果在啟用調試層的情況下運行應用程序,則應用程序將顯著變慢。

要創建支持調試層的設備,必須安裝DirectX SDK(以獲取D3D11SDKLayers.dll)

  渲染管線輸出:


  渲染管線並不能將渲染結果直接輸出到窗口,只能輸出到一個叫渲染目標視圖(ID3D11RenderTargetView)的對象,在創建 ID3D11RenderTargetView 時需要和一個紋理對象綁定。可以使用交換鏈的 GetBuffer 方法來獲取交換鏈後臺紋理緩沖區指針,並創建一個渲染目標視圖,最後將該視圖設置到渲染管線,就可以將渲染管線的結果呈現給窗口了。

    /* 獲取交換鏈的後緩沖 */
    ID3D11Texture2D* pBackBuffer = NULL;
    hr = g_pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), ( LPVOID* ) &pBackBuffer);
    if ( FAILED(hr) ) return hr;

    /* 創建渲染目標視圖 */
    hr = g_pd3dDevice->CreateRenderTargetView(pBackBuffer, NULL, &g_pRenderTargetView);
    pBackBuffer->Release();
    if ( FAILED(hr) ) return hr;

    g_pImmediateContext->OMSetRenderTargets(1, &g_pRenderTargetView, 0);

  

  使用函數 OMSetRenderTargets 設置渲染目標視圖到渲染管線時,可以設置 1 - D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT 之間數量的渲染目標視圖。默認情況下使用第一個渲染目標視圖,如果你要使用其它的渲染目標視圖,必須在像素著色器中映射管線多輸出值到 SV_Target[n] (其中 n 介於 0D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT 之間)語義,實現多渲染目標(MRT)。像素著色器代碼如下:

struct PS_OUTPUT
{
    float4 color0 : SV_Target0;
    float4 color1 : SV_Target1;
};

//--------------------------------------------------------------------------------------
// Pixel Shader
//--------------------------------------------------------------------------------------
PS_OUTPUT PS( VS_OUTPUT input )
{
    PS_OUTPUT o;

    o.color0 = input.Color;
    o.color1 = float4(1, 0, 0, 1);

    return o;
}

  因為沒有多少文章介紹 DirectX11 多渲染目標(MRT)的實現,所以在這裏簡單介紹一下。

  視口(ViewPort):


  然不是嚴格考慮Direct3D初始化階段的一部分,但設置視口定義是初始化光柵化器階段的必要組件。視口定義了我們的最終渲染將進入的屏幕空間區域。對於這個應用程序,我們將渲染到應用程序窗口的整個客戶區,但是如果我們想實現分屏多人或畫中畫效果,我們也可以定義兩個視口。

    D3D11_VIEWPORT view_port;

    view_port.Width = ( FLOAT ) width;
    view_port.Height = ( FLOAT ) height;
    view_port.MinDepth = 0.0f;
    view_port.MaxDepth = 1.0f;
    view_port.TopLeftX = 0;
    view_port.TopLeftY = 0;

    g_pImmediateContext->RSSetViewports(1, &view_port);

  

  使用 RSSetViewports 設置視口到渲染管線時,可以設置 1 - D3D11_VIEWPORT_AND_SCISSORRECT_MAX_INDEX 之間數量的視口。默認情況下使用第一個視口,如果你想使用其它的視口,必須在幾何著色器中設置 SV_ViewportArrayIndex 語義確定輸出到哪個視口。如果想將窗口或分為 4 部分,先創建 4 個視口:

    D3D11_VIEWPORT view_port[4];
    for ( int i = 0; i < 4; i++ )
    {
        view_port[i].Width = ( FLOAT ) width / 2;
        view_port[i].Height = ( FLOAT ) height / 2;
        view_port[i].MinDepth = 0.0f;
        view_port[i].MaxDepth = 1.0f;
        view_port[i].TopLeftX = 0;
        view_port[i].TopLeftY = 0;
    }
    view_port[1].TopLeftX = ( FLOAT ) width / 2;
    view_port[1].TopLeftY = 0;

    view_port[2].TopLeftX = 0;
    view_port[2].TopLeftY = ( FLOAT ) height / 2;

    view_port[3].TopLeftX = ( FLOAT ) width / 2;
    view_port[3].TopLeftY = ( FLOAT ) height / 2;
    g_pImmediateContext->RSSetViewports(4, view_port);

  接著是幾何著色器的設置:

//--------------------------------------------------------------------------------------
// Constant Buffer Variables
//--------------------------------------------------------------------------------------
cbuffer ConstantBuffer : register( b0 )
{
    matrix World;
    matrix View;
    matrix Projection;
}

//--------------------------------------------------------------------------------------
struct VS_OUTPUT
{
    float4 Pos : SV_POSITION;
    float4 Color : COLOR0;
};


//--------------------------------------------------------------------------------------
struct GS_OUTPUT
{
    float4 Pos : SV_POSITION;
    float4 Color : COLOR0;
    uint view_idx : SV_VIEWPORTARRAYINDEX;
};

//--------------------------------------------------------------------------------------
// Vertex Shader
//--------------------------------------------------------------------------------------
VS_OUTPUT VS( float4 Pos : POSITION, float4 Color : COLOR )
{
    VS_OUTPUT output = (VS_OUTPUT)0;
    output.Pos = mul( Pos, World );
    output.Pos = mul( output.Pos, View );
    output.Pos = mul( output.Pos, Projection );
    output.Color = Color;
    return output;
}

//--------------------------------------------------------------------------------------
// Geometry Shader
//--------------------------------------------------------------------------------------
[maxvertexcount(12)]  
void GS(triangle VS_OUTPUT input[3], inout TriangleStream<GS_OUTPUT> output)  
{  
    for(int j = 0; j < 4; j++)
    {
        GS_OUTPUT element;  
        element.view_idx = j;

        for (uint i = 0; i < 3;i++)  
        {  
           element.Pos = input[i].Pos;  
           element.Color = input[i].Color;  
           output.Append(element);  
        }  
        output.RestartStrip();
    }
}


//--------------------------------------------------------------------------------------
// Pixel Shader
//--------------------------------------------------------------------------------------
float4 PS( VS_OUTPUT input ) : SV_Target
{
    return input.Color;
}

  在編譯並設置幾何著色器到渲染管線後,得到以下結果:

技術分享圖片

  因為沒有什麽文章介紹這種方法的實現,所以在這裏簡單介紹一下。當然你要可以不這樣做,你可以渲染 4 次,每次使用不同的視口,得到和上面一樣的結果。

  源碼下載:MMDViewer 04.zip

基於 DirectX11 的 MMDViewer 04-渲染目標視圖和多視口