Direct3D 11 - Initialization
Direct3D 11 初始化步骤
- 使用D3D11CreateDevice 函数,创建 ID3D11Device 和 ID3D11DeviceContext
ID3D11Device 和 ID3D11DeviceContext 接口是 Direct3D 最重要的接口,认为是图形硬件的软件控制器(software controller),也就是说可以通过这两个接口可以控制图形硬件完成制定工作(比如,申请GPU内存资源,清除后台缓冲区,绑定资源到管线的各个阶段,绘制几何图元),更准确地说,
1. ID3D11Device 接口用于 check feature support,allocate resource
2. ID3D11DeviceContext 接口用于 set render states,bind resource to the graphics pipeline,issue rendering commands
device 和 context 由下面的函数创建:
HRESULT D3D11CreateDevice( _In_opt_ IDXGIAdapter *pAdapter, D3D_DRIVER_TYPE DriverType, HMODULE Software, UINT Flags, _In_opt_ const D3D_FEATURE_LEVEL *pFeatureLevels, UINT FeatureLevels, UINT SDKVersion, _Out_opt_ ID3D11Device **ppDevice, _Out_opt_ D3D_FEATURE_LEVEL *pFeatureLevel, _Out_opt_ ID3D11DeviceContext **ppImmediateContext );
参数含义,详见MSDN SDK文档
创建示例如下:
// Create the device and device context. UINT createDeviceFlags = 0; #if defined(DEBUG) || defined(_DEBUG) createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG; #endif D3D_FEATURE_LEVEL featureLevel; HRESULT hr = D3D11CreateDevice( 0, // default adapter md3dDriverType, 0, // no software device createDeviceFlags, 0, 0, // default feature level array 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 Feature Level 11 unsupported.", 0, 0); return false; }
- 检查 4X MSAA Quality Support
因为Direct3D 11 默认要支持4X多重采样,所以多重采样质量总大于0。
// Check 4X MSAA quality support for our back buffer format. // All Direct3D 11 capable devices support 4X MSAA for all render // target formats, so we only need to check quality support. HR(md3dDevice->CheckMultisampleQualityLevels( DXGI_FORMAT_R8G8B8A8_UNORM, 4, &m4xMsaaQuality)); assert( m4xMsaaQuality > 0 );
- 描述 Swap Chain
首先我们需要填充一个 DXGI_SWAP_CHAIN_DESC 结构体,用来描述所要创建的 swap chain 的属性,DXGI_SWAP_CHAIN_DESC 定义如下:
typedef struct DXGI_SWAP_CHAIN_DESC { DXGI_MODE_DESC BufferDesc; DXGI_SAMPLE_DESC SampleDesc; DXGI_USAGE BufferUsage; UINT BufferCount; HWND OutputWindow; BOOL Windowed; DXGI_SWAP_EFFECT SwapEffect; UINT Flags; } DXGI_SWAP_CHAIN_DESC; // DXGI_MODE_DESC Definition typedef struct DXGI_MODE_DESC { UINT Width; UINT Height; DXGI_RATIONAL RefreshRate; DXGI_FORMAT Format; DXGI_MODE_SCANLINE_ORDER ScanlineOrdering; DXGI_MODE_SCALING Scaling; } DXGI_MODE_DESC;
定义含义,详见MSDN SDK文档:DXGI_SWAP_CHAIN_DESC, DXGI_MODE_DESC
示例代码:
// Fill out a DXGI_SWAP_CHAIN_DESC to describe our swap chain. 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; // Use 4X MSAA? if( mEnable4xMsaa ) { sd.SampleDesc.Count = 4; sd.SampleDesc.Quality = m4xMsaaQuality-1; } // No MSAA 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;
- 创建 Swap Chain
// To correctly create the swap chain, we must use the IDXGIFactory that was // used to create the device. If we tried to use a different IDXGIFactory instance // (by calling CreateDXGIFactory), we get an error: "IDXGIFactory::CreateSwapChain: // This function is being called with a device from a different IDXGIFactory." IDXGIDevice* dxgiDevice = 0; HR(md3dDevice->QueryInterface(__uuidof(IDXGIDevice), (void**)&dxgiDevice)); IDXGIAdapter* dxgiAdapter = 0; HR(dxgiDevice->GetParent(__uuidof(IDXGIAdapter), (void**)&dxgiAdapter)); IDXGIFactory* dxgiFactory = 0; HR(dxgiAdapter->GetParent(__uuidof(IDXGIFactory), (void**)&dxgiFactory)); HR(dxgiFactory->CreateSwapChain(md3dDevice, &sd, &mSwapChain)); ReleaseCOM(dxgiDevice); ReleaseCOM(dxgiAdapter); ReleaseCOM(dxgiFactory);
- 创建 Render Target View
因为我们不能将 resource 直接绑定到渲染管线的一个阶段,而只能将 resource view 绑定到管线的一个阶段。所以,为了将back buffer 绑定到 output merge stage,我们需要创建一个后台缓冲区的视图,如下,
ID3D11RenderTargetView* mRenderTargetView; ID3D11Texture2D* backBuffer; mSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), // 0 表示后台缓冲区的索引地址,因为现在只有一块后台缓冲区,所以设置为0 reinterpret_cast<void**>(&backBuffer)); md3dDevice->CreateRenderTargetView(backBuffer, 0, &mRenderTargetView); ReleaseCOM(backBuffer);
- 创建 深度/模板 缓冲区和视图
我们需要创建 深度/模板 缓冲区。深度缓冲区是一块保存深度数据的2D纹理。为了创建一块2D Texture,首先需要填充 D3D11_TEXTURE2D_DESC 结构体,然后调用 ID3D11Device::CreateTexture2D 方法。
typedef struct D3D11_TEXTURE2D_DESC { UINT Width; UINT Height; UINT MipLevels; UINT ArraySize; DXGI_FORMAT Format; DXGI_SAMPLE_DESC SampleDesc; D3D11_USAGE Usage; UINT BindFlags; UINT CPUAccessFlags; UINT MiscFlags; } D3D11_TEXTURE2D_DESC;
结构体每一个成员的含义见MSDN SDK文档
示例代码:
// Create the depth/stencil buffer and view. D3D11_TEXTURE2D_DESC depthStencilDesc; depthStencilDesc.Width = mClientWidth; depthStencilDesc.Height = mClientHeight; depthStencilDesc.MipLevels = 1; depthStencilDesc.ArraySize = 1; depthStencilDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT; // Use 4X MSAA? --must match swap chain MSAA values. if( mEnable4xMsaa ) { depthStencilDesc.SampleDesc.Count = 4; depthStencilDesc.SampleDesc.Quality = m4xMsaaQuality-1; } // No MSAA else { depthStencilDesc.SampleDesc.Count = 1; depthStencilDesc.SampleDesc.Quality = 0; } depthStencilDesc.Usage = D3D11_USAGE_DEFAULT; depthStencilDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL; depthStencilDesc.CPUAccessFlags = 0; depthStencilDesc.MiscFlags = 0; HR(md3dDevice->CreateTexture2D(&depthStencilDesc, 0, &mDepthStencilBuffer));
创建 depth/stencil buffer 之后,创建视图:
HR(md3dDevice->CreateDepthStencilView( mDepthStencilBuffer, // Resource we want to create a view to 0, // D3D11_DEPTH_STENCIL_VIEW_DESC, 因为创建 depth/stencil buffer view,所以可以指定为0 &mDepthStencilView)); // Return depth/stencil view
- 将渲染目标视图和深度/模板视图绑定到渲染管线的 Output Merge Stage
// Bind the render target view and depth/stencil view to the pipeline. md3dImmediateContext->OMSetRenderTargets(1, &mRenderTargetView, mDepthStencilView);
第一个参数表示要绑定的render target 的个数,这里只绑定一个render target 。我们可以将这些视图绑定到多个渲染目标。第二个参数是将要绑定的渲染目标数组的第一个元素的指针。第三个参数是将要绑定到管线的 depth/stencil buffer view。注意,只能设置一个深度/模板视图。
- 设置Viewport
通常情况,我们会把3D场景渲染到整个back buffer中,但是,有时候我们想只绘制back buffer的一个子矩形区域中,后台缓冲的子矩形区域叫作视口(Viewport)。设置适口需要先填充一个 D3D11_VIEWPORT 结构体,示例代码如下:
// Set the viewport transform. mScreenViewport.TopLeftX = 0; mScreenViewport.TopLeftY = 0; mScreenViewport.Width = static_cast<float>(mClientWidth); mScreenViewport.Height = static_cast<float>(mClientHeight); mScreenViewport.MinDepth = 0.0f; mScreenViewport.MaxDepth = 1.0f; md3dImmediateContext->RSSetViewports(1, &mScreenViewport);
D3D11_VIEWPORT 结构体每一个成员的含义,见MSDN SDK文档
goudan-er GRAPHICS
Graphics D3D11