上篇文章給出了一個簡單並且可以運行的渲染框架,接下來將介紹框架中的渲染管線構成。
1、創建渲染管線
在你創建完一個窗口后,接着便要創建渲染管線,使用的函數是 D3D11CreateDeviceAndSwapChain,
交換鏈:
要創建交換鏈,必須先設置交換鏈描述。交換鏈描述定義了將由交換鏈使用的渲染緩沖區的大小和數量。它還將窗口與交換鏈相關聯,從而確定最終圖像的顯示位置。交換鏈描述還定義了該應用的消除鋸齒(如果有的話)的質量以及在展示過程中后端緩沖區的翻轉方式。
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 介於 0 和 D3D11_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