009-Direct3D 12 初始化详解:从设备创建到渲染框架构建

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 的工作原理:

  1. 对每个像素位置采集多个样本(通常为 2、4 或 8 个)
  2. 对每个样本独立执行深度测试和模板测试
  3. 对通过测试的样本应用像素着色器,然后将结果混合

配置 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 等高级功能
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小宝哥Code

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值