Direct3D 12 初始化详解:从设备创建到渲染框架构建
Direct3D 是 DirectX 图形 API 的核心组件,负责 3D 图形渲染。在实际开发 DirectX 应用程序时,正确初始化 Direct3D 是所有后续渲染操作的基础。本章将详细介绍 Direct3D 12 的初始化过程及相关概念。
4.1 预备知识
在深入 Direct3D 初始化之前,我们需要了解一些基本概念和编程模型。
4.1.1 Direct3D 12 概述
Direct3D 12 是微软推出的低级图形 API,与之前版本相比具有如下特点:
- 低开销:减少了 CPU 负担,提高了多核 CPU 利用率
- 明确控制:开发者对 GPU 内存和执行方式有更精确的控制
- 多线程友好:允许从多个 CPU 线程并行提交工作
- 性能优化:通过减少驱动程序开销提升性能
Direct3D 12 遵循与 Vulkan、Metal 等现代图形 API 类似的设计理念,让开发者直接管理资源和渲染管线状态,从而获得更高的性能和更低的延迟。
cpp
// Direct3D 12 中常用的头文件
#include <d3d12.h> // Direct3D 12 核心 API
#include <dxgi1_6.h> // DirectX 图形基础设施
#include <d3dcompiler.h> // 着色器编译器
#include <DirectXMath.h> // 数学库
#include <wrl/client.h> // ComPtr 智能指针
4.1.2 组件对象模型
Direct3D 和 DirectX 的其他部分采用 COM (Component Object Model) 接口进行交互。COM 是一种语言无关的组件模型,允许不同编程语言创建的软件组件相互交互。
COM 对象的关键特点:
- 引用计数:对象维护一个引用计数,当计数为零时自动释放
- 接口查询:允许查询对象支持的接口
- 跨语言兼容:不同语言编写的组件可以相互使用
在 C++ 中,可以使用 Microsoft 的 ComPtr
智能指针管理 COM 对象的生命周期:
cpp
// 使用 ComPtr 智能指针管理 COM 对象
using Microsoft::WRL::ComPtr;
ComPtr<ID3D12Device> d3dDevice;
HRESULT hr = D3D12CreateDevice(
adapter.Get(),
D3D_FEATURE_LEVEL_12_0,
IID_PPV_ARGS(&d3dDevice)
);
// ComPtr 会自动调用 Release(),无需手动释放
4.1.3 纹理格式
Direct3D 使用 DXGI_FORMAT
枚举表示纹理和缓冲区格式。常用的格式包括:
- DXGI_FORMAT_R8G8B8A8_UNORM:8 位无符号标准化的 RGBA 格式
- DXGI_FORMAT_R32G32B32A32_FLOAT:32 位浮点 RGBA 格式
- DXGI_FORMAT_D24_UNORM_S8_UINT:24 位深度,8 位模板格式
- DXGI_FORMAT_R16G16_FLOAT:16 位浮点 RG 格式
选择合适的格式对于性能和视觉质量至关重要:
cpp
// 创建纹理资源时指定格式
D3D12_RESOURCE_DESC texDesc = {};
texDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; // 常用的 RGBA 格式
texDesc.Width = width;
texDesc.Height = height;
texDesc.DepthOrArraySize = 1;
texDesc.MipLevels = 1;
texDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
texDesc.SampleDesc.Count = 1;
4.1.4 交换链和页面翻转
交换链管理用于显示的缓冲区集合,通常采用双缓冲(或三缓冲)策略。基本概念包括:
- 后台缓冲区:渲染图像的缓冲区
- 前台缓冲区:当前显示在屏幕上的缓冲区
- 翻转:交换前台和后台缓冲区,使新渲染的内容显示出来
交换链创建过程:
cpp
// 创建交换链
ComPtr<IDXGISwapChain3> swapChain;
DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {};
swapChainDesc.Width = width;
swapChainDesc.Height = height;
swapChainDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
swapChainDesc.SampleDesc.Count = 1;
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapChainDesc.BufferCount = 2; // 双缓冲
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
ComPtr<IDXGISwapChain1> swapChain1;
factory->CreateSwapChainForHwnd(
commandQueue.Get(),
hwnd,
&swapChainDesc,
nullptr,
nullptr,
&swapChain1
);
// 转换为 IDXGISwapChain3 接口
swapChain1.As(&swapChain);
页面翻转通常在每帧渲染结束时进行:
cpp
// 呈现后台缓冲区内容
swapChain->Present(1, 0); // 1=垂直同步,0=无标志
4.1.5 深度缓冲
深度缓冲存储每个像素的深度值,用于确定哪些物体可见,哪些被遮挡。正确配置深度缓冲对于 3D 渲染至关重要。
创建深度缓冲资源:
cpp
// 创建深度缓冲区描述符
D3D12_RESOURCE_DESC depthStencilDesc = {};
depthStencilDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
depthStencilDesc.Width = width;
depthStencilDesc.Height = height;
depthStencilDesc.DepthOrArraySize = 1;
depthStencilDesc.MipLevels = 1;
depthStencilDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
depthStencilDesc.SampleDesc.Count = 1;
depthStencilDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;
// 优化为深度写入
D3D12_HEAP_PROPERTIES heapProps = {};
heapProps.Type = D3D12_HEAP_TYPE_DEFAULT;
// 清除值(深度为 1.0,模板为 0)
D3D12_CLEAR_VALUE clearValue = {};
clearValue.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
clearValue.DepthStencil.Depth = 1.0f;
clearValue.DepthStencil.Stencil = 0;
// 创建深度缓冲区资源
ComPtr<ID3D12Resource> depthStencilBuffer;
device->CreateCommittedResource(
&heapProps,
D3D12_HEAP_FLAG_NONE,
&depthStencilDesc,
D3D12_RESOURCE_STATE_DEPTH_WRITE,
&clearValue,
IID_PPV_ARGS(&depthStencilBuffer)
);
在渲染管线中使用深度缓冲:
cpp
// 配置深度模板状态
D3D12_DEPTH_STENCIL_DESC depthStencilDesc = {};
depthStencilDesc.DepthEnable = TRUE; // 启用深度测试
depthStencilDesc.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL; // 写入所有深度值
depthStencilDesc.DepthFunc = D3D12_COMPARISON_FUNC_LESS; // 深度值小的像素可见
// 在 PSO 创建时设置深度模板状态
D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {};
// ... 设置其他管线状态 ...
psoDesc.DepthStencilState = depthStencilDesc;
psoDesc.DSVFormat = DXGI_FORMAT_D24_UNORM_S8_UINT;
4.1.6 资源与描述符
在 Direct3D 12 中,资源是 GPU 使用的内存对象(如纹理、缓冲区等),而描述符则是这些资源的"视图",告诉 GPU 如何使用资源。
主要资源类型:
- 缓冲区资源:顶点缓冲区、索引缓冲区、常量缓冲区等
- 纹理资源:2D 纹理、3D 纹理、立方体贴图等
主要描述符类型:
- CBV(Constant Buffer View):常量缓冲区视图
- SRV(Shader Resource View):着色器资源视图,用于从纹理中采样
- UAV(Unordered Access View):无序访问视图,用于计算着色器写入
- RTV(Render Target View):渲染目标视图,用于输出颜色
- DSV(Depth Stencil View):深度模板视图,用于深度测试
创建资源和描述符:
cpp
// 创建顶点缓冲区
ComPtr<ID3D12Resource> vertexBuffer;
D3D12_HEAP_PROPERTIES heapProps = {};
heapProps.Type = D3D12_HEAP_TYPE_UPLOAD; // CPU 可写,GPU 可读
D3D12_RESOURCE_DESC resourceDesc = {};
resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
resourceDesc.Width = vertexBufferSize;
resourceDesc.Height = 1;
resourceDesc.DepthOrArraySize = 1;
resourceDesc.MipLevels = 1;
resourceDesc.Format = DXGI_FORMAT_UNKNOWN;
resourceDesc.SampleDesc.Count = 1;
resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
device->CreateCommittedResource(
&heapProps,
D3D12_HEAP_FLAG_NONE,
&resourceDesc,
D3D12_RESOURCE_STATE_GENERIC_READ,
nullptr,
IID_PPV_ARGS(&vertexBuffer)
);
// 创建顶点缓冲区视图(VBV)
D3D12_VERTEX_BUFFER_VIEW vbv = {};
vbv.BufferLocation = vertexBuffer->GetGPUVirtualAddress();
vbv.SizeInBytes = vertexBufferSize;
vbv.StrideInBytes = sizeof(Vertex); // 每个顶点的大小
描述符堆是存储描述符的内存区域:
cpp
// 创建 RTV 描述符堆
ComPtr<ID3D12DescriptorHeap> rtvHeap;
D3D12_DESCRIPTOR_HEAP_DESC rtvHeapDesc = {};
rtvHeapDesc.NumDescriptors = 2; // 双缓冲,每个后台缓冲区一个 RTV
rtvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
rtvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
device->CreateDescriptorHeap(&rtvHeapDesc, IID_PPV_ARGS(&rtvHeap));
// 创建渲染目标视图(RTV)
CD3DX12_CPU_DESCRIPTOR_HANDLE rtvHandle(rtvHeap->GetCPUDescriptorHandleForHeapStart());
UINT rtvDescriptorSize = device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
for (UINT i = 0; i < 2; i++) { // 为每个后台缓冲区创建 RTV
swapChain->GetBuffer(i, IID_PPV_ARGS(&renderTargets[i]));
device->CreateRenderTargetView(renderTargets[i].Get(), nullptr, rtvHandle);
rtvHandle.Offset(1, rtvDescriptorSize);
}
4.1.7 多重采样技术的原理
多重采样抗锯齿(MSAA)是一种提高渲染质量的技术,通过在每个像素内采样多个点并计算平均值来减少锯齿边缘。
MSAA 的工作原理:
- 对每个像素位置采集多个样本(通常为 2、4 或 8 个)
- 对每个样本独立执行深度测试和模板测试
- 对通过测试的样本应用像素着色器,然后将结果混合
配置 MSAA:
cpp
// 检查多重采样质量级别支持
UINT sampleCount = 4; // 4X MSAA
UINT qualityLevels;
device->CheckFeatureSupport(
D3D12_FEATURE_MULTISAMPLE_QUALITY_LEVELS,
&msaaQualityLevels,
sizeof(msaaQualityLevels)
);
// 确保支持 4X MSAA
UINT qualityLevel = qualityLevels.NumQualityLevels - 1;
assert(qualityLevel > 0);
// 创建启用 MSAA 的资源
D3D12_RESOURCE_DESC msaaResourceDesc = {};
msaaResourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
msaaResourceDesc.Width = width;
msaaResourceDesc.Height = height;
msaaResourceDesc.DepthOrArraySize = 1;
msaaResourceDesc.MipLevels = 1;
msaaResourceDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
msaaResourceDesc.SampleDesc.Count = sampleCount;
msaaResourceDesc.SampleDesc.Quality = qualityLevel;
msaaResourceDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
4.1.8 利用 Direct3D 进行多重采样
在 Direct3D 12 中实现 MSAA 需要创建多重采样资源和解析(Resolve)过程:
cpp
// 创建 MSAA 渲染目标
ComPtr<ID3D12Resource> msaaRenderTarget;
D3D12_HEAP_PROPERTIES defaultProps = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT);
D3D12_CLEAR_VALUE clearValue = {};
clearValue.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
clearValue.Color[0] = 0.0f;
clearValue.Color[1] = 0.0f;
clearValue.Color[2] = 0.0f;
clearValue.Color[3] = 1.0f;
device->CreateCommittedResource(
&defaultProps,
D3D12_HEAP_FLAG_NONE,
&msaaResourceDesc,
D3D12_RESOURCE_STATE_RESOLVE_SOURCE,
&clearValue,
IID_PPV_ARGS(&msaaRenderTarget)
);
// 创建 MSAA 渲染目标视图
device->CreateRenderTargetView(
msaaRenderTarget.Get(),
nullptr,
msaaRtvHandle
);
// 渲染到 MSAA 渲染目标
// ...渲染代码...
// 解析 MSAA 图像到非 MSAA 资源(通常是交换链的后台缓冲区)
commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(
msaaRenderTarget.Get(),
D3D12_RESOURCE_STATE_RENDER_TARGET,
D3D12_RESOURCE_STATE_RESOLVE_SOURCE
));
commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(
renderTargets[frameIndex].Get(),
D3D12_RESOURCE_STATE_PRESENT,
D3D12_RESOURCE_STATE_RESOLVE_DEST
));
// 解析 MSAA 采样
commandList->ResolveSubresource(
renderTargets[frameIndex].Get(), 0,
msaaRenderTarget.Get(), 0,
DXGI_FORMAT_R8G8B8A8_UNORM
);
4.1.9 功能级别
Direct3D 功能级别定义了硬件支持的功能集。Direct3D 12 支持以下功能级别:
- D3D_FEATURE_LEVEL_12_1:支持 Conservative Rasterization Tier 3 等高级功能