第13章 Direct3D核心编程精要——从初始化到渲染流水线
13.1 Direct3D初始化深度解析与商业实践
13.1.1 Direct3D设备创建的艺术
在商业游戏开发中,Direct3D设备的创建不仅仅是一个技术步骤,更是性能和兼容性的平衡艺术。以《巫师3:狂猎》为例,这款游戏需要支持从DirectX 9到DirectX 11的各种硬件配置,因此其设备创建逻辑必须足够健壮和灵活。
Windows游戏编程理论中,设备创建的核心是理解特性级别(Feature Level)的概念。特性级别定义了GPU支持的功能集,从9.1到11.0,每个级别都对应着不同的硬件能力。商业游戏通常会从最高级别开始尝试,然后逐步降级直到找到合适的级别。
下面是一个商业级的设备创建函数,它考虑了多种硬件配置和错误处理:
#include <windows.h>
#include <d3d11.h>
#include <dxgi.h>
#include <vector>
#include <string>
#pragma comment(lib, "d3d11.lib")
#pragma comment(lib, "dxgi.lib")
class Direct3DDeviceManager
{
private:
struct GPUInfo
{
std::wstring description;
UINT dedicatedVideoMemory;
UINT dedicatedSystemMemory;
UINT sharedSystemMemory;
bool isPrimaryAdapter;
};
std::vector<GPUInfo> gpuList;
IDXGIFactory* dxgiFactory;
IDXGIAdapter* selectedAdapter;
ID3D11Device* direct3DDevice;
ID3D11DeviceContext* deviceContext;
D3D_FEATURE_LEVEL selectedFeatureLevel;
public:
Direct3DDeviceManager()
: dxgiFactory(nullptr)
, selectedAdapter(nullptr)
, direct3DDevice(nullptr)
, deviceContext(nullptr)
, selectedFeatureLevel(D3D_FEATURE_LEVEL_9_1)
{
}
bool Initialize(bool enableDebugLayer = false)
{
HRESULT result = S_OK;
// 1. 创建DXGI工厂
result = CreateDXGIFactory(__uuidof(IDXGIFactory), (void**)&dxgiFactory);
if (FAILED(result))
{
LogError(L"创建DXGI工厂失败");
return false;
}
// 2. 枚举所有GPU并选择最佳适配器
if (!EnumerateAndSelectGPU())
{
return false;
}
// 3. 定义特性级别数组(从高到低尝试)
D3D_FEATURE_LEVEL featureLevels[] =
{
D3D_FEATURE_LEVEL_11_1, // Windows 8及更高版本
D3D_FEATURE_LEVEL_11_0, // Windows 7及更高版本
D3D_FEATURE_LEVEL_10_1, // Windows Vista SP2及更高版本
D3D_FEATURE_LEVEL_10_0, // Windows Vista及更高版本
D3D_FEATURE_LEVEL_9_3, // Windows 7及更高版本
D3D_FEATURE_LEVEL_9_2, // Windows 7及更高版本
D3D_FEATURE_LEVEL_9_1 // Windows 7及更高版本
};
// 4. 创建标志
UINT creationFlags = 0;
if (enableDebugLayer)
{
creationFlags |= D3D11_CREATE_DEVICE_DEBUG;
}
// 5. 创建设备和上下文
result = D3D11CreateDevice(
selectedAdapter,
selectedAdapter ? D3D_DRIVER_TYPE_UNKNOWN : D3D_DRIVER_TYPE_HARDWARE,
NULL,
creationFlags,
featureLevels,
ARRAYSIZE(featureLevels),
D3D11_SDK_VERSION,
&direct3DDevice,
&selectedFeatureLevel,
&deviceContext
);
// 6. 如果使用硬件适配器失败,尝试使用WARP软件适配器
if (FAILED(result))
{
LogWarning(L"硬件设备创建失败,尝试WARP软件设备");
result = D3D11CreateDevice(
NULL,
D3D_DRIVER_TYPE_WARP, // WARP软件渲染器
NULL,
creationFlags,
featureLevels,
ARRAYSIZE(featureLevels),
D3D11_SDK_VERSION,
&direct3DDevice,
&selectedFeatureLevel,
&deviceContext
);
if (FAILED(result))
{
LogError(L"WARP设备创建也失败");
return false;
}
}
// 7. 输出设备信息
LogDeviceInfo();
return true;
}
bool EnumerateAndSelectGPU()
{
IDXGIAdapter* adapter = nullptr;
UINT adapterIndex = 0;
GPUInfo bestAdapter;
bool foundBest = false;
// 枚举所有适配器
while (dxgiFactory->EnumAdapters(adapterIndex, &adapter) != DXGI_ERROR_NOT_FOUND)
{
GPUInfo gpuInfo;
DXGI_ADAPTER_DESC adapterDesc;
if (SUCCEEDED(adapter->GetDesc(&adapterDesc)))
{
gpuInfo.description = adapterDesc.Description;
gpuInfo.dedicatedVideoMemory = adapterDesc.DedicatedVideoMemory;
gpuInfo.dedicatedSystemMemory = adapterDesc.DedicatedSystemMemory;
gpuInfo.sharedSystemMemory = adapterDesc.SharedSystemMemory;
gpuInfo.isPrimaryAdapter = (adapterIndex == 0);
gpuList.push_back(gpuInfo);
// 选择策略:优先选择专用显存最大的GPU
if (!foundBest || gpuInfo.dedicatedVideoMemory > bestAdapter.dedicatedVideoMemory)
{
bestAdapter = gpuInfo;
foundBest = true;
// 更新选中的适配器
if (selectedAdapter)
{
selectedAdapter->Release();
}
adapter->AddRef();
selectedAdapter = adapter;
}
}
adapter->Release();
adapterIndex++;
}
if (!foundBest)
{
LogWarning(L"未找到合适的GPU适配器,将使用默认适配器");
// 使用默认适配器(NULL表示使用默认)
selectedAdapter = nullptr;
}
return true;
}
void LogDeviceInfo()
{
std::wstring featureLevelStr;
switch (selectedFeatureLevel)
{
case D3D_FEATURE_LEVEL_11_1:
featureLevelStr = L"Direct3D 11.1";
break;
case D3D_FEATURE_LEVEL_11_0:
featureLevelStr = L"Direct3D 11.0";
break;
case D3D_FEATURE_LEVEL_10_1:
featureLevelStr = L"Direct3D 10.1";
break;
case D3D_FEATURE_LEVEL_10_0:
featureLevelStr = L"Direct3D 10.0";
break;
case D3D_FEATURE_LEVEL_9_3:
featureLevelStr = L"Direct3D 9.3";
break;
case D3D_FEATURE_LEVEL_9_2:
featureLevelStr = L"Direct3D 9.2";
break;
case D3D_FEATURE_LEVEL_9_1:
featureLevelStr = L"Direct3D 9.1";
break;
default:
featureLevelStr = L"Unknown";
}
std::wstring logMessage = L"Direct3D设备创建成功\\n";
logMessage += L"特性级别: " + featureLevelStr + L"\\n";
if (!gpuList.empty())
{
logMessage += L"GPU数量: " + std::to_wstring(gpuList.size()) + L"\\n";
for (size_t i = 0; i < gpuList.size(); ++i)
{
logMessage += L"GPU " + std::to_wstring(i) + L": " + gpuList[i].description + L"\\n";
logMessage += L" 专用显存: " + std::to_wstring(gpuList[i].dedicatedVideoMemory / (1024 * 1024)) + L" MB\\n";
logMessage += L" 专用系统内存: " + std::to_wstring(gpuList[i].dedicatedSystemMemory / (1024 * 1024)) + L" MB\\n";
logMessage += L" 共享系统内存: " + std::to_wstring(gpuList[i].sharedSystemMemory / (1024 * 1024)) + L" MB\\n";
}
}
OutputDebugString(logMessage.c_str());
}
void LogError(const std::wstring& message)
{
std::wstring errorMessage = L"[ERROR] " + message + L"\\n";
OutputDebugString(errorMessage.c_str());
}
void LogWarning(const std::wstring& message)
{
std::wstring warningMessage = L"[WARNING] " + message + L"\\n";
OutputDebugString(warningMessage.c_str());
}
void Cleanup()
{
if (deviceContext)
{
deviceContext->Release();
deviceContext = nullptr;
}
if (direct3DDevice)
{
direct3DDevice->Release();
direct3DDevice = nullptr;
}
if (selectedAdapter)
{
selectedAdapter->Release();
selectedAdapter = nullptr;
}
if (dxgiFactory)
{
dxgiFactory->Release();
dxgiFactory = nullptr;
}
}
ID3D11Device* GetDevice() const { return direct3DDevice; }
ID3D11DeviceContext* GetDeviceContext() const { return deviceContext; }
D3D_FEATURE_LEVEL GetFeatureLevel() const { return selectedFeatureLevel; }
~Direct3DDeviceManager()
{
Cleanup();
}
};
这个设备管理器展示了商业游戏开发中的最佳实践:
13.1.2 交换链配置的商业考量
交换链配置直接影响游戏的视觉体验和性能。在《赛博朋克2077》等现代游戏中,玩家可以在多种显示模式间切换:全屏独占模式、无边框窗口模式和窗口模式。每种模式都有其优缺点。
让我们深入探讨交换链配置的各个方面:
#include <windows.h>
#include <d3d11.h>
#include <dxgi.h>
#include <string>
#pragma comment(lib, "d3d11.lib")
#pragma comment(lib, "dxgi.lib")
enum class DisplayMode
{
FullscreenExclusive, // 全屏独占模式(最佳性能)
BorderlessWindow, // 无边框窗口模式(Alt+Tab切换流畅)
Windowed // 窗口模式(兼容性最好)
};
enum class RefreshRateMode
{
VSyncEnabled, // 垂直同步开启(防止撕裂,可能限制帧率)
VSyncDisabled, // 垂直同步关闭(最大帧率,可能撕裂)
AdaptiveSync // 自适应同步(G-Sync/FreeSync)
};
class SwapChainManager
{
private:
IDXGISwapChain* swapChain;
DisplayMode currentDisplayMode;
RefreshRateMode currentRefreshRateMode;
// 配置参数
UINT width;
UINT height;
DXGI_FORMAT format;
UINT bufferCount;
UINT sampleCount;
UINT sampleQuality;
HWND windowHandle;
// 全屏相关
bool isFullscreen;
DXGI_MODE_DESC fullscreenMode;
public:
SwapChainManager()
: swapChain(nullptr)
, currentDisplayMode(DisplayMode::Windowed)
, currentRefreshRateMode(RefreshRateMode::VSyncEnabled)
, width(0)
, height(0)
, format(DXGI_FORMAT_R8G8B8A8_UNORM)
, bufferCount(2) // 双缓冲
, sampleCount(1)
, sampleQuality(0)
, windowHandle(nullptr)
, isFullscreen(false)
{
ZeroMemory(&fullscreenMode, sizeof(fullscreenMode));
}
bool Initialize(
ID3D11Device* device,
HWND hwnd,
UINT initialWidth,
UINT initialHeight,
DisplayMode displayMode = DisplayMode::Windowed,
RefreshRateMode refreshRateMode = RefreshRateMode::VSyncEnabled)
{
windowHandle = hwnd;
width = initialWidth;
height = initialHeight;
currentDisplayMode = displayMode;
currentRefreshRateMode = refreshRateMode;
// 获取DXGI工厂(通过设备查询)
IDXGIDevice* dxgiDevice = nullptr;
HRESULT result = device->QueryInterface(__uuidof(IDXGIDevice), (void**)&dxgiDevice);
if (FAILED(result))
{
return false;
}
IDXGIAdapter* adapter = nullptr;
result = dxgiDevice->GetAdapter(&adapter);
dxgiDevice->Release();
if (FAILED(result))
{
return false;
}
IDXGIFactory* factory = nullptr;
result = adapter->GetParent(__uuidof(IDXGIFactory), (void**)&factory);
adapter->Release();
if (FAILED(result))
{
return false;
}
// 根据显示模式创建交换链
DXGI_SWAP_CHAIN_DESC swapChainDesc;
ZeroMemory(&swapChainDesc, sizeof(swapChainDesc));
// 基本配置
swapChainDesc.BufferDesc.Width = width;
swapChainDesc.BufferDesc.Height = height;
swapChainDesc.BufferDesc.Format = format;
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapChainDesc.BufferCount = bufferCount;
swapChainDesc.OutputWindow = windowHandle;
swapChainDesc.Windowed = (displayMode != DisplayMode::FullscreenExclusive);
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
swapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
// 采样配置
swapChainDesc.SampleDesc.Count = sampleCount;
swapChainDesc.SampleDesc.Quality = sampleQuality;
// 刷新率配置
ConfigureRefreshRate(swapChainDesc.BufferDesc);
// 根据显示模式进行特殊配置
if (displayMode == DisplayMode::BorderlessWindow)
{
// 无边框窗口模式需要特殊处理
ConfigureBorderlessWindow(factory, swapChainDesc);
}
// 创建交换链
result = factory->CreateSwapChain(device, &swapChainDesc, &swapChain);
if (displayMode == DisplayMode::FullscreenExclusive && SUCCEEDED(result))
{
// 设置为全屏模式
swapChain->SetFullscreenState(TRUE, NULL);
// 获取当前显示模式
IDXGIOutput* output = nullptr;
result = swapChain->GetContainingOutput(&output);
if (SUCCEEDED(result) && output)
{
DXGI_MODE_DESC currentMode;
result = swapChain->GetDesc(&swapChainDesc);
if (SUCCEEDED(result))
{
fullscreenMode = swapChainDesc.BufferDesc;
isFullscreen = true;
}
output->Release();
}
}
factory->Release();
return SUCCEEDED(result);
}
void ConfigureRefreshRate(DXGI_MODE_DESC& bufferDesc)
{
switch (currentRefreshRateMode)
{
case RefreshRateMode::VSyncEnabled:
// 开启垂直同步,使用显示器的原生刷新率
bufferDesc.RefreshRate.Numerator = 60; // 默认60Hz
bufferDesc.RefreshRate.Denominator = 1;
break;
case RefreshRateMode::VSyncDisabled:
// 关闭垂直同步,最大帧率
bufferDesc.RefreshRate.Numerator = 0;
bufferDesc.RefreshRate.Denominator = 1;
break;
case RefreshRateMode::AdaptiveSync:
// 自适应同步,需要检测显示器支持
if (IsAdaptiveSyncSupported())
{
// 设置自适应同步参数
bufferDesc.RefreshRate.Numerator = 0;
bufferDesc.RefreshRate.Denominator = 1;
// 这里需要设置特殊的交换链标志
// DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING (Windows 10及更高版本)
}
else
{
// 不支持自适应同步,回退到垂直同步
bufferDesc.RefreshRate.Numerator = 60;
bufferDesc.RefreshRate.Denominator = 1;
}
break;
}
}
void ConfigureBorderlessWindow(IDXGIFactory* factory, DXGI_SWAP_CHAIN_DESC& swapChainDesc)
{
// 获取主显示器的尺寸
IDXGIOutput* primaryOutput = nullptr;
if (SUCCEEDED(factory->EnumOutputs(0, &primaryOutput)) && primaryOutput)
{
DXGI_OUTPUT_DESC outputDesc;
if (SUCCEEDED(primaryOutput->GetDesc(&outputDesc)))
{
// 将窗口设置为覆盖整个显示器
width = outputDesc.DesktopCoordinates.right – outputDesc.DesktopCoordinates.left;
height = outputDesc.DesktopCoordinates.bottom – outputDesc.DesktopCoordinates.top;
// 调整交换链尺寸
swapChainDesc.BufferDesc.Width = width;
swapChainDesc.BufferDesc.Height = height;
// 设置窗口样式为无边框
SetWindowLong(windowHandle, GWL_STYLE, WS_POPUP | WS_VISIBLE);
SetWindowPos(windowHandle, HWND_TOP,
outputDesc.DesktopCoordinates.left,
outputDesc.DesktopCoordinates.top,
width, height,
SWP_FRAMECHANGED);
}
primaryOutput->Release();
}
}
bool IsAdaptiveSyncSupported()
{
// 在实际应用中,这里需要检测G-Sync或FreeSync支持
// 简化版本:总是返回false
return false;
}
bool Resize(UINT newWidth, UINT newHeight)
{
if (!swapChain)
{
return false;
}
// 释放现有的渲染目标视图等资源
// (这里假设调用者已经释放了相关资源)
width = newWidth;
height = newHeight;
HRESULT result = swapChain->ResizeBuffers(
bufferCount,
width,
height,
format,
DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH
);
return SUCCEEDED(result);
}
bool ToggleFullscreen()
{
if (!swapChain)
{
return false;
}
BOOL isCurrentlyFullscreen = FALSE;
IDXGIOutput* currentOutput = nullptr;
// 获取当前全屏状态
swapChain->GetFullscreenState(&isCurrentlyFullscreen, ¤tOutput);
// 切换全屏状态
HRESULT result = swapChain->SetFullscreenState(!isCurrentlyFullscreen, currentOutput);
if (currentOutput)
{
currentOutput->Release();
}
if (SUCCEEDED(result))
{
isFullscreen = !isCurrentlyFullscreen;
if (isFullscreen)
{
currentDisplayMode = DisplayMode::FullscreenExclusive;
}
else
{
currentDisplayMode = DisplayMode::Windowed;
}
}
return SUCCEEDED(result);
}
void SetRefreshRateMode(RefreshRateMode mode)
{
if (currentRefreshRateMode == mode)
{
return;
}
currentRefreshRateMode = mode;
// 需要重新创建交换链来应用新的刷新率设置
// 在实际应用中,这里应该保存当前状态并重新初始化
}
void Present(UINT syncInterval = 1, UINT flags = 0)
{
if (!swapChain)
{
return;
}
// 根据刷新率模式调整呈现参数
UINT presentFlags = flags;
switch (currentRefreshRateMode)
{
case RefreshRateMode::VSyncEnabled:
syncInterval = 1; // 等待垂直同步
break;
case RefreshRateMode::VSyncDisabled:
syncInterval = 0; // 立即呈现
break;
case RefreshRateMode::AdaptiveSync:
if (IsAdaptiveSyncSupported())
{
syncInterval = 0;
presentFlags |= DXGI_PRESENT_ALLOW_TEARING;
}
else
{
syncInterval = 1;
}
break;
}
swapChain->Present(syncInterval, presentFlags);
}
void Cleanup()
{
if (swapChain)
{
// 切换到窗口模式再释放,避免全屏状态的问题
swapChain->SetFullscreenState(FALSE, NULL);
swapChain->Release();
swapChain = nullptr;
}
}
IDXGISwapChain* GetSwapChain() const { return swapChain; }
UINT GetWidth() const { return width; }
UINT GetHeight() const { return height; }
DisplayMode GetDisplayMode() const { return currentDisplayMode; }
RefreshRateMode GetRefreshRateMode() const { return currentRefreshRateMode; }
~SwapChainManager()
{
Cleanup();
}
};
这个交换链管理器处理了现代游戏中的多种显示配置需求:
13.1.3 深度模板缓冲区的商业级实现
深度模板缓冲区对于3D游戏的正确渲染至关重要。在《荒野大镖客2》等开放世界游戏中,复杂的场景需要高效的深度管理来确保正确的物体遮挡关系。
#include <windows.h>
#include <d3d11.h>
#include <dxgi.h>
#include <vector>
#pragma comment(lib, "d3d11.lib")
class DepthStencilManager
{
private:
struct DepthStencilConfig
{
DXGI_FORMAT format;
UINT width;
UINT height;
UINT sampleCount;
UINT sampleQuality;
bool enableDepthTest;
bool enableStencilTest;
D3D11_COMPARISON_FUNC depthComparisonFunc;
D3D11_DEPTH_WRITE_MASK depthWriteMask;
UINT stencilReadMask;
UINT stencilWriteMask;
};
ID3D11Device* device;
ID3D11DeviceContext* deviceContext;
// 深度模板资源
ID3D11Texture2D* depthStencilTexture;
ID3D11DepthStencilView* depthStencilView;
ID3D11DepthStencilState* depthStencilState;
// 配置
DepthStencilConfig currentConfig;
// 多重深度缓冲区(用于特殊效果,如深度剥离)
std::vector<ID3D11Texture2D*> multiDepthTextures;
std::vector<ID3D11DepthStencilView*> multiDepthViews;
public:
DepthStencilManager(ID3D11Device* dev, ID3D11DeviceContext* context)
: device(dev)
, deviceContext(context)
, depthStencilTexture(nullptr)
, depthStencilView(nullptr)
, depthStencilState(nullptr)
{
// 默认配置
currentConfig.format = DXGI_FORMAT_D24_UNORM_S8_UINT;
currentConfig.width = 0;
currentConfig.height = 0;
currentConfig.sampleCount = 1;
currentConfig.sampleQuality = 0;
currentConfig.enableDepthTest = true;
currentConfig.enableStencilTest = false;
currentConfig.depthComparisonFunc = D3D11_COMPARISON_LESS;
currentConfig.depthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
currentConfig.stencilReadMask = 0xFF;
currentConfig.stencilWriteMask = 0xFF;
}
bool Initialize(UINT width, UINT height, UINT sampleCount = 1, UINT sampleQuality = 0)
{
currentConfig.width = width;
currentConfig.height = height;
currentConfig.sampleCount = sampleCount;
currentConfig.sampleQuality = sampleQuality;
return CreateDepthStencilResources();
}
bool CreateDepthStencilResources()
{
// 清理现有资源
ReleaseResources();
// 创建深度模板纹理
D3D11_TEXTURE2D_DESC depthStencilDesc;
ZeroMemory(&depthStencilDesc, sizeof(depthStencilDesc));
depthStencilDesc.Width = currentConfig.width;
depthStencilDesc.Height = currentConfig.height;
depthStencilDesc.MipLevels = 1;
depthStencilDesc.ArraySize = 1;
depthStencilDesc.Format = currentConfig.format;
depthStencilDesc.SampleDesc.Count = currentConfig.sampleCount;
depthStencilDesc.SampleDesc.Quality = currentConfig.sampleQuality;
depthStencilDesc.Usage = D3D11_USAGE_DEFAULT;
depthStencilDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
depthStencilDesc.CPUAccessFlags = 0;
depthStencilDesc.MiscFlags = 0;
HRESULT result = device->CreateTexture2D(&depthStencilDesc, NULL, &depthStencilTexture);
if (FAILED(result))
{
return false;
}
// 创建深度模板视图
D3D11_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc;
ZeroMemory(&depthStencilViewDesc, sizeof(depthStencilViewDesc));
depthStencilViewDesc.Format = currentConfig.format;
if (currentConfig.sampleCount > 1)
{
depthStencilViewDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DMS;
}
else
{
depthStencilViewDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
depthStencilViewDesc.Texture2D.MipSlice = 0;
}
result = device->CreateDepthStencilView(depthStencilTexture, &depthStencilViewDesc, &depthStencilView);
if (FAILED(result))
{
return false;
}
// 创建深度模板状态
D3D11_DEPTH_STENCIL_DESC depthStencilStateDesc;
ZeroMemory(&depthStencilStateDesc, sizeof(depthStencilStateDesc));
// 深度测试配置
depthStencilStateDesc.DepthEnable = currentConfig.enableDepthTest;
depthStencilStateDesc.DepthWriteMask = currentConfig.depthWriteMask;
depthStencilStateDesc.DepthFunc = currentConfig.depthComparisonFunc;
// 模板测试配置
depthStencilStateDesc.StencilEnable = currentConfig.enableStencilTest;
depthStencilStateDesc.StencilReadMask = currentConfig.stencilReadMask;
depthStencilStateDesc.StencilWriteMask = currentConfig.stencilWriteMask;
// 正面多边形模板操作
depthStencilStateDesc.FrontFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
depthStencilStateDesc.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_INCR;
depthStencilStateDesc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
depthStencilStateDesc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS;
// 背面多边形模板操作
depthStencilStateDesc.BackFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
depthStencilStateDesc.BackFace.StencilDepthFailOp = D3D11_STENCIL_OP_DECR;
depthStencilStateDesc.BackFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
depthStencilStateDesc.BackFace.StencilFunc = D3D11_COMPARISON_ALWAYS;
result = device->CreateDepthStencilState(&depthStencilStateDesc, &depthStencilState);
if (FAILED(result))
{
return false;
}
// 设置深度模板状态
deviceContext->OMSetDepthStencilState(depthStencilState, 1);
return true;
}
bool CreateMultiDepthBuffers(UINT count, DXGI_FORMAT format = DXGI_FORMAT_R32_FLOAT)
{
// 清理现有的多重深度缓冲区
for (auto& texture : multiDepthTextures)
{
if (texture) texture->Release();
}
multiDepthTextures.clear();
for (auto& view : multiDepthViews)
{
if (view) view->Release();
}
multiDepthViews.clear();
// 创建多个深度缓冲区(用于深度剥离等高级技术)
for (UINT i = 0; i < count; ++i)
{
ID3D11Texture2D* depthTexture = nullptr;
ID3D11DepthStencilView* depthView = nullptr;
// 创建纹理
D3D11_TEXTURE2D_DESC texDesc;
ZeroMemory(&texDesc, sizeof(texDesc));
texDesc.Width = currentConfig.width;
texDesc.Height = currentConfig.height;
texDesc.MipLevels = 1;
texDesc.ArraySize = 1;
texDesc.Format = format;
texDesc.SampleDesc.Count = 1;
texDesc.SampleDesc.Quality = 0;
texDesc.Usage = D3D11_USAGE_DEFAULT;
texDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL | D3D11_BIND_SHADER_RESOURCE;
texDesc.CPUAccessFlags = 0;
texDesc.MiscFlags = 0;
HRESULT result = device->CreateTexture2D(&texDesc, NULL, &depthTexture);
if (FAILED(result))
{
// 清理已创建的资源
for (auto& texture : multiDepthTextures)
{
if (texture) texture->Release();
}
for (auto& view : multiDepthViews)
{
if (view) view->Release();
}
return false;
}
// 创建视图
D3D11_DEPTH_STENCIL_VIEW_DESC dsvDesc;
ZeroMemory(&dsvDesc, sizeof(dsvDesc));
dsvDesc.Format = DXGI_FORMAT_D32_FLOAT; // 深度格式
dsvDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
dsvDesc.Texture2D.MipSlice = 0;
result = device->CreateDepthStencilView(depthTexture, &dsvDesc, &depthView);
if (FAILED(result))
{
depthTexture->Release();
// 清理已创建的资源
for (auto& texture : multiDepthTextures)
{
if (texture) texture->Release();
}
for (auto& view : multiDepthViews)
{
if (view) view->Release();
}
return false;
}
multiDepthTextures.push_back(depthTexture);
multiDepthViews.push_back(depthView);
}
return true;
}
void Clear(float depthValue = 1.0f, UINT8 stencilValue = 0)
{
if (depthStencilView)
{
UINT clearFlags = 0;
if (currentConfig.enableDepthTest)
{
clearFlags |= D3D11_CLEAR_DEPTH;
}
if (currentConfig.enableStencilTest)
{
clearFlags |= D3D11_CLEAR_STENCIL;
}
if (clearFlags != 0)
{
deviceContext->ClearDepthStencilView(depthStencilView, clearFlags, depthValue, stencilValue);
}
}
// 清除多重深度缓冲区
for (auto& view : multiDepthViews)
{
deviceContext->ClearDepthStencilView(view, D3D11_CLEAR_DEPTH, depthValue, stencilValue);
}
}
void SetDepthTestEnabled(bool enabled)
{
if (currentConfig.enableDepthTest == enabled)
{
return;
}
currentConfig.enableDepthTest = enabled;
// 重新创建深度模板状态
if (depthStencilState)
{
depthStencilState->Release();
depthStencilState = nullptr;
}
D3D11_DEPTH_STENCIL_DESC depthStencilStateDesc;
ZeroMemory(&depthStencilStateDesc, sizeof(depthStencilStateDesc));
depthStencilStateDesc.DepthEnable = enabled;
depthStencilStateDesc.DepthWriteMask = currentConfig.depthWriteMask;
depthStencilStateDesc.DepthFunc = currentConfig.depthComparisonFunc;
// … 其他配置
device->CreateDepthStencilState(&depthStencilStateDesc, &depthStencilState);
deviceContext->OMSetDepthStencilState(depthStencilState, 1);
}
void SetDepthComparisonFunc(D3D11_COMPARISON_FUNC func)
{
if (currentConfig.depthComparisonFunc == func)
{
return;
}
currentConfig.depthComparisonFunc = func;
// 重新创建深度模板状态
if (depthStencilState)
{
depthStencilState->Release();
depthStencilState = nullptr;
}
D3D11_DEPTH_STENCIL_DESC depthStencilStateDesc;
ZeroMemory(&depthStencilStateDesc, sizeof(depthStencilStateDesc));
depthStencilStateDesc.DepthEnable = currentConfig.enableDepthTest;
depthStencilStateDesc.DepthWriteMask = currentConfig.depthWriteMask;
depthStencilStateDesc.DepthFunc = func;
// … 其他配置
device->CreateDepthStencilState(&depthStencilStateDesc, &depthStencilState);
deviceContext->OMSetDepthStencilState(depthStencilState, 1);
}
void Resize(UINT newWidth, UINT newHeight)
{
if (newWidth == currentConfig.width && newHeight == currentConfig.height)
{
return;
}
currentConfig.width = newWidth;
currentConfig.height = newHeight;
// 重新创建资源
CreateDepthStencilResources();
// 重新创建多重深度缓冲区(如果存在)
if (!multiDepthTextures.empty())
{
CreateMultiDepthBuffers(static_cast<UINT>(multiDepthTextures.size()));
}
}
void ReleaseResources()
{
if (depthStencilState)
{
depthStencilState->Release();
depthStencilState = nullptr;
}
if (depthStencilView)
{
depthStencilView->Release();
depthStencilView = nullptr;
}
if (depthStencilTexture)
{
depthStencilTexture->Release();
depthStencilTexture = nullptr;
}
// 释放多重深度缓冲区
for (auto& texture : multiDepthTextures)
{
if (texture) texture->Release();
}
multiDepthTextures.clear();
for (auto& view : multiDepthViews)
{
if (view) view->Release();
}
multiDepthViews.clear();
}
ID3D11DepthStencilView* GetDepthStencilView() const { return depthStencilView; }
ID3D11DepthStencilState* GetDepthStencilState() const { return depthStencilState; }
UINT GetWidth() const { return currentConfig.width; }
UINT GetHeight() const { return currentConfig.height; }
~DepthStencilManager()
{
ReleaseResources();
}
};
这个深度模板管理器提供了以下高级功能:
13.2 Direct3D渲染流水线完整实现
13.2.1 现代渲染流水线架构
现代游戏如《最终幻想7重制版》使用复杂的渲染流水线,包括前向渲染、延迟渲染、前向+等多种技术。理解这些流水线架构对于商业游戏开发至关重要。
下面实现一个灵活的渲染流水线管理器:
#include <windows.h>
#include <d3d11.h>
#include <d3dcompiler.h>
#include <DirectXMath.h>
#include <vector>
#include <map>
#include <string>
#pragma comment(lib, "d3d11.lib")
#pragma comment(lib, "d3dcompiler.lib")
using namespace DirectX;
enum class RenderPipelineType
{
Forward, // 前向渲染(传统方式)
Deferred, // 延迟渲染(现代AAA游戏常用)
ForwardPlus, // 前向+渲染(平衡方案)
TileBased // 基于分块的延迟渲染(移动平台)
};
enum class LightingModel
{
Phong, // Phong光照模型
BlinnPhong, // Blinn-Phong光照模型(更高效)
PBR, // 基于物理的渲染(现代标准)
CelShading // 卡通渲染
};
struct PipelineConfig
{
RenderPipelineType pipelineType;
LightingModel lightingModel;
bool enableShadows;
bool enableSSAO; // 屏幕空间环境光遮蔽
bool enableBloom; // 泛光效果
bool enableMotionBlur; // 运动模糊
bool enableDepthOfField; // 景深效果
UINT shadowMapSize;
UINT msaaLevel;
};
class RenderPipelineManager
{
private:
ID3D11Device* device;
ID3D11DeviceContext* deviceContext;
// 当前配置
PipelineConfig currentConfig;
// 渲染目标
ID3D11RenderTargetView* mainRenderTargetView;
ID3D11Texture2D* mainRenderTargetTexture;
// G-Buffer用于延迟渲染
struct GBuffer
{
ID3D11Texture2D* texture;
ID3D11RenderTargetView* rtv;
ID3D11ShaderResourceView* srv;
DXGI_FORMAT format;
};
std::vector<GBuffer> gBuffer;
// 深度缓冲区
ID3D11Texture2D* depthStencilTexture;
ID3D11DepthStencilView* depthStencilView;
ID3D11ShaderResourceView* depthSRV;
// 着色器
ID3D11VertexShader* forwardVS;
ID3D11PixelShader* forwardPS;
ID3D11VertexShader* deferredVS;
ID3D11PixelShader* deferredPS;
ID3D11PixelShader* lightingPS;
// 输入布局
ID3D11InputLayout* inputLayout;
// 常量和采样器
ID3D11Buffer* perFrameConstantBuffer;
ID3D11Buffer* perObjectConstantBuffer;
ID3D11SamplerState* linearSampler;
ID3D11SamplerState* pointSampler;
// 视口
D3D11_VIEWPORT mainViewport;
public:
RenderPipelineManager(ID3D11Device* dev, ID3D11DeviceContext* context)
: device(dev)
, deviceContext(context)
, mainRenderTargetView(nullptr)
, mainRenderTargetTexture(nullptr)
, depthStencilTexture(nullptr)
, depthStencilView(nullptr)
, depthSRV(nullptr)
, forwardVS(nullptr)
, forwardPS(nullptr)
, deferredVS(nullptr)
, deferredPS(nullptr)
, lightingPS(nullptr)
, inputLayout(nullptr)
, perFrameConstantBuffer(nullptr)
, perObjectConstantBuffer(nullptr)
, linearSampler(nullptr)
, pointSampler(nullptr)
{
// 默认配置
currentConfig.pipelineType = RenderPipelineType::Forward;
currentConfig.lightingModel = LightingModel::BlinnPhong;
currentConfig.enableShadows = true;
currentConfig.enableSSAO = true;
currentConfig.enableBloom = false;
currentConfig.enableMotionBlur = false;
currentConfig.enableDepthOfField = false;
currentConfig.shadowMapSize = 2048;
currentConfig.msaaLevel = 1;
}
bool Initialize(UINT width, UINT height, PipelineConfig config)
{
currentConfig = config;
// 创建视口
mainViewport.TopLeftX = 0.0f;
mainViewport.TopLeftY = 0.0f;
mainViewport.Width = static_cast<float>(width);
mainViewport.Height = static_cast<float>(height);
mainViewport.MinDepth = 0.0f;
mainViewport.MaxDepth = 1.0f;
// 创建主渲染目标
if (!CreateMainRenderTarget(width, height))
{
return false;
}
// 创建深度缓冲区
if (!CreateDepthStencil(width, height))
{
return false;
}
// 创建G-Buffer(如果是延迟渲染)
if (config.pipelineType == RenderPipelineType::Deferred ||
config.pipelineType == RenderPipelineType::ForwardPlus)
{
if (!CreateGBuffer(width, height))
{
return false;
}
}
// 编译着色器
if (!CompileShaders())
{
return false;
}
// 创建常量缓冲区
if (!CreateConstantBuffers())
{
return false;
}
// 创建采样器状态
if (!CreateSamplerStates())
{
return false;
}
return true;
}
bool CreateMainRenderTarget(UINT width, UINT height)
{
// 创建主渲染目标纹理
D3D11_TEXTURE2D_DESC textureDesc;
ZeroMemory(&textureDesc, sizeof(textureDesc));
textureDesc.Width = width;
textureDesc.Height = height;
textureDesc.MipLevels = 1;
textureDesc.ArraySize = 1;
textureDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
textureDesc.SampleDesc.Count = currentConfig.msaaLevel;
textureDesc.SampleDesc.Quality = 0;
textureDesc.Usage = D3D11_USAGE_DEFAULT;
textureDesc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
textureDesc.CPUAccessFlags = 0;
textureDesc.MiscFlags = 0;
HRESULT result = device->CreateTexture2D(&textureDesc, NULL, &mainRenderTargetTexture);
if (FAILED(result))
{
return false;
}
// 创建渲染目标视图
result = device->CreateRenderTargetView(mainRenderTargetTexture, NULL, &mainRenderTargetView);
return SUCCEEDED(result);
}
bool CreateDepthStencil(UINT width, UINT height)
{
// 创建深度模板纹理
D3D11_TEXTURE2D_DESC depthStencilDesc;
ZeroMemory(&depthStencilDesc, sizeof(depthStencilDesc));
depthStencilDesc.Width = width;
depthStencilDesc.Height = height;
depthStencilDesc.MipLevels = 1;
depthStencilDesc.ArraySize = 1;
depthStencilDesc.Format = DXGI_FORMAT_R24G8_TYPELESS; // 用于创建SRV
depthStencilDesc.SampleDesc.Count = currentConfig.msaaLevel;
depthStencilDesc.SampleDesc.Quality = 0;
depthStencilDesc.Usage = D3D11_USAGE_DEFAULT;
depthStencilDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL | D3D11_BIND_SHADER_RESOURCE;
depthStencilDesc.CPUAccessFlags = 0;
depthStencilDesc.MiscFlags = 0;
HRESULT result = device->CreateTexture2D(&depthStencilDesc, NULL, &depthStencilTexture);
if (FAILED(result))
{
return false;
}
// 创建深度模板视图
D3D11_DEPTH_STENCIL_VIEW_DESC dsvDesc;
ZeroMemory(&dsvDesc, sizeof(dsvDesc));
dsvDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
if (currentConfig.msaaLevel > 1)
{
dsvDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DMS;
}
else
{
dsvDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
dsvDesc.Texture2D.MipSlice = 0;
}
result = device->CreateDepthStencilView(depthStencilTexture, &dsvDesc, &depthStencilView);
if (FAILED(result))
{
return false;
}
// 创建深度纹理的着色器资源视图(用于后处理)
D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
ZeroMemory(&srvDesc, sizeof(srvDesc));
srvDesc.Format = DXGI_FORMAT_R24_UNORM_X8_TYPELESS;
if (currentConfig.msaaLevel > 1)
{
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DMS;
}
else
{
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
srvDesc.Texture2D.MostDetailedMip = 0;
srvDesc.Texture2D.MipLevels = 1;
}
result = device->CreateShaderResourceView(depthStencilTexture, &srvDesc, &depthSRV);
return SUCCEEDED(result);
}
bool CreateGBuffer(UINT width, UINT height)
{
// G-Buffer通常包含:
// 1. 漫反射颜色 + 材质类型
// 2. 法线 + 粗糙度
// 3. 位置 + 金属度
// 4. 自发光 + 其他数据
DXGI_FORMAT gBufferFormats[] =
{
DXGI_FORMAT_R8G8B8A8_UNORM, // 漫反射 + 材质
DXGI_FORMAT_R16G16B16A16_FLOAT, // 法线 + 粗糙度
DXGI_FORMAT_R32G32B32A32_FLOAT, // 位置 + 金属度
DXGI_FORMAT_R8G8B8A8_UNORM // 自发光 + 其他
};
const char* gBufferNames[] =
{
"DiffuseMaterial",
"NormalRoughness",
"PositionMetalness",
"EmissiveOther"
};
for (int i = 0; i < 4; ++i)
{
GBuffer buffer;
// 创建纹理
D3D11_TEXTURE2D_DESC texDesc;
ZeroMemory(&texDesc, sizeof(texDesc));
texDesc.Width = width;
texDesc.Height = height;
texDesc.MipLevels = 1;
texDesc.ArraySize = 1;
texDesc.Format = gBufferFormats[i];
texDesc.SampleDesc.Count = 1; // G-Buffer通常不使用多重采样
texDesc.SampleDesc.Quality = 0;
texDesc.Usage = D3D11_USAGE_DEFAULT;
texDesc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
texDesc.CPUAccessFlags = 0;
texDesc.MiscFlags = 0;
HRESULT result = device->CreateTexture2D(&texDesc, NULL, &buffer.texture);
if (FAILED(result))
{
// 清理已创建的缓冲区
for (auto& gb : gBuffer)
{
if (gb.srv) gb.srv->Release();
if (gb.rtv) gb.rtv->Release();
if (gb.texture) gb.texture->Release();
}
gBuffer.clear();
return false;
}
// 创建渲染目标视图
result = device->CreateRenderTargetView(buffer.texture, NULL, &buffer.rtv);
if (FAILED(result))
{
buffer.texture->Release();
// 清理已创建的缓冲区
for (auto& gb : gBuffer)
{
if (gb.srv) gb.srv->Release();
if (gb.rtv) gb.rtv->Release();
if (gb.texture) gb.texture->Release();
}
gBuffer.clear();
return false;
}
// 创建着色器资源视图
result = device->CreateShaderResourceView(buffer.texture, NULL, &buffer.srv);
if (FAILED(result))
{
buffer.rtv->Release();
buffer.texture->Release();
// 清理已创建的缓冲区
for (auto& gb : gBuffer)
{
if (gb.srv) gb.srv->Release();
if (gb.rtv) gb.rtv->Release();
if (gb.texture) gb.texture->Release();
}
gBuffer.clear();
return false;
}
buffer.format = gBufferFormats[i];
gBuffer.push_back(buffer);
}
return true;
}
bool CompileShaders()
{
HRESULT result = S_OK;
// 编译前向着色器
const char* forwardVSCode =
"cbuffer PerFrameConstants : register(b0)\\n"
"{\\n"
" float4x4 viewMatrix;\\n"
" float4x4 projectionMatrix;\\n"
" float3 cameraPosition;\\n"
" float padding;\\n"
"};\\n"
"\\n"
"cbuffer PerObjectConstants : register(b1)\\n"
"{\\n"
" float4x4 worldMatrix;\\n"
" float4 materialColor;\\n"
" float roughness;\\n"
" float metalness;\\n"
" float2 paddingObj;\\n"
"};\\n"
"\\n"
"struct VertexInput\\n"
"{\\n"
" float3 position : POSITION;\\n"
" float3 normal : NORMAL;\\n"
" float2 texcoord : TEXCOORD;\\n"
"};\\n"
"\\n"
"struct PixelInput\\n"
"{\\n"
" float4 position : SV_POSITION;\\n"
" float3 worldPosition : POSITION;\\n"
" float3 normal : NORMAL;\\n"
" float2 texcoord : TEXCOORD;\\n"
"};\\n"
"\\n"
"PixelInput main(VertexInput input)\\n"
"{\\n"
" PixelInput output;\\n"
" \\n"
" // 世界变换\\n"
" float4 worldPos = mul(float4(input.position, 1.0f), worldMatrix);\\n"
" \\n"
" // 视图和投影变换\\n"
" output.position = mul(mul(worldPos, viewMatrix), projectionMatrix);\\n"
" \\n"
" // 传递其他数据\\n"
" output.worldPosition = worldPos.xyz;\\n"
" output.normal = normalize(mul(input.normal, (float3x3)worldMatrix));\\n"
" output.texcoord = input.texcoord;\\n"
" \\n"
" return output;\\n"
"}";
const char* forwardPSCode =
"cbuffer PerFrameConstants : register(b0)\\n"
"{\\n"
" float4x4 viewMatrix;\\n"
" float4x4 projectionMatrix;\\n"
" float3 cameraPosition;\\n"
" float padding;\\n"
"};\\n"
"\\n"
"cbuffer PerObjectConstants : register(b1)\\n"
"{\\n"
" float4x4 worldMatrix;\\n"
" float4 materialColor;\\n"
" float roughness;\\n"
" float metalness;\\n"
" float2 paddingObj;\\n"
"};\\n"
"\\n"
"struct PixelInput\\n"
"{\\n"
" float4 position : SV_POSITION;\\n"
" float3 worldPosition : POSITION;\\n"
" float3 normal : NORMAL;\\n"
" float2 texcoord : TEXCOORD;\\n"
"};\\n"
"\\n"
"// 简单光照计算(Blinn-Phong)\\n"
"float3 CalculateLighting(float3 position, float3 normal, float3 viewDir)\\n"
"{\\n"
" float3 lightColor = float3(1.0f, 1.0f, 1.0f);\\n"
" float3 lightDirection = normalize(float3(1.0f, 1.0f, -1.0f));\\n"
" \\n"
" // 漫反射\\n"
" float diffuse = max(dot(normal, lightDirection), 0.0f);\\n"
" float3 diffuseColor = lightColor * diffuse;\\n"
" \\n"
" // 镜面反射(Blinn-Phong)\\n"
" float3 halfVector = normalize(lightDirection + viewDir);\\n"
" float specular = pow(max(dot(normal, halfVector), 0.0f), 32.0f);\\n"
" float3 specularColor = lightColor * specular;\\n"
" \\n"
" // 环境光\\n"
" float3 ambientColor = float3(0.1f, 0.1f, 0.1f);\\n"
" \\n"
" return ambientColor + diffuseColor + specularColor;\\n"
"}\\n"
"\\n"
"float4 main(PixelInput input) : SV_TARGET\\n"
"{\\n"
" // 标准化法线\\n"
" float3 normal = normalize(input.normal);\\n"
" \\n"
" // 计算视线方向\\n"
" float3 viewDir = normalize(cameraPosition – input.worldPosition);\\n"
" \\n"
" // 计算光照\\n"
" float3 lighting = CalculateLighting(input.worldPosition, normal, viewDir);\\n"
" \\n"
" // 应用材质颜色\\n"
" float3 finalColor = materialColor.rgb * lighting;\\n"
" \\n"
" return float4(finalColor, materialColor.a);\\n"
"}";
ID3DBlob* vsBlob = nullptr;
ID3DBlob* psBlob = nullptr;
ID3DBlob* errorBlob = nullptr;
// 编译顶点着色器
result = D3DCompile(
forwardVSCode,
strlen(forwardVSCode),
"ForwardVS",
nullptr,
nullptr,
"main",
"vs_5_0",
0,
0,
&vsBlob,
&errorBlob
);
if (FAILED(result))
{
if (errorBlob)
{
OutputDebugStringA((char*)errorBlob->GetBufferPointer());
errorBlob->Release();
}
return false;
}
// 创建顶点着色器
result = device->CreateVertexShader(vsBlob->GetBufferPointer(), vsBlob->GetBufferSize(), nullptr, &forwardVS);
if (FAILED(result))
{
vsBlob->Release();
return false;
}
// 编译像素着色器
result = D3DCompile(
forwardPSCode,
strlen(forwardPSCode),
"ForwardPS",
nullptr,
nullptr,
"main",
"ps_5_0",
0,
0,
&psBlob,
&errorBlob
);
if (FAILED(result))
{
vsBlob->Release();
if (errorBlob)
{
errorBlob->Release();
}
return false;
}
// 创建像素着色器
result = device->CreatePixelShader(psBlob->GetBufferPointer(), psBlob->GetBufferSize(), nullptr, &forwardPS);
// 创建输入布局
D3D11_INPUT_ELEMENT_DESC layout[] =
{
{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 24, D3D11_INPUT_PER_VERTEX_DATA, 0 }
};
result = device->CreateInputLayout(
layout,
3,
vsBlob->GetBufferPointer(),
vsBlob->GetBufferSize(),
&inputLayout
);
// 清理临时对象
vsBlob->Release();
psBlob->Release();
if (errorBlob)
{
errorBlob->Release();
}
return SUCCEEDED(result);
}
bool CreateConstantBuffers()
{
// 创建每帧常量缓冲区
D3D11_BUFFER_DESC perFrameDesc;
ZeroMemory(&perFrameDesc, sizeof(perFrameDesc));
perFrameDesc.Usage = D3D11_USAGE_DYNAMIC;
perFrameDesc.ByteWidth = sizeof(PerFrameConstants);
perFrameDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
perFrameDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
HRESULT result = device->CreateBuffer(&perFrameDesc, NULL, &perFrameConstantBuffer);
if (FAILED(result))
{
return false;
}
// 创建每个对象常量缓冲区
D3D11_BUFFER_DESC perObjectDesc;
ZeroMemory(&perObjectDesc, sizeof(perObjectDesc));
perObjectDesc.Usage = D3D11_USAGE_DYNAMIC;
perObjectDesc.ByteWidth = sizeof(PerObjectConstants);
perObjectDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
perObjectDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
result = device->CreateBuffer(&perObjectDesc, NULL, &perObjectConstantBuffer);
return SUCCEEDED(result);
}
bool CreateSamplerStates()
{
// 创建线性采样器
D3D11_SAMPLER_DESC linearDesc;
ZeroMemory(&linearDesc, sizeof(linearDesc));
linearDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
linearDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
linearDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
linearDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
linearDesc.MipLODBias = 0.0f;
linearDesc.MaxAnisotropy = 1;
linearDesc.ComparisonFunc = D3D11_COMPARISON_ALWAYS;
linearDesc.BorderColor[0] = 0;
linearDesc.BorderColor[1] = 0;
linearDesc.BorderColor[2] = 0;
linearDesc.BorderColor[3] = 0;
linearDesc.MinLOD = 0;
linearDesc.MaxLOD = D3D11_FLOAT32_MAX;
HRESULT result = device->CreateSamplerState(&linearDesc, &linearSampler);
if (FAILED(result))
{
return false;
}
// 创建点采样器
D3D11_SAMPLER_DESC pointDesc;
ZeroMemory(&pointDesc, sizeof(pointDesc));
pointDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT;
pointDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
pointDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
pointDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
pointDesc.MipLODBias = 0.0f;
pointDesc.MaxAnisotropy = 1;
pointDesc.ComparisonFunc = D3D11_COMPARISON_ALWAYS;
pointDesc.BorderColor[0] = 0;
pointDesc.BorderColor[1] = 0;
pointDesc.BorderColor[2] = 0;
pointDesc.BorderColor[3] = 0;
pointDesc.MinLOD = 0;
pointDesc.MaxLOD = D3D11_FLOAT32_MAX;
result = device->CreateSamplerState(&pointDesc, &pointSampler);
return SUCCEEDED(result);
}
void BeginFrame(const float clearColor[4])
{
// 设置视口
deviceContext->RSSetViewports(1, &mainViewport);
// 清除主渲染目标
deviceContext->ClearRenderTargetView(mainRenderTargetView, clearColor);
deviceContext->ClearDepthStencilView(depthStencilView, D3D11_CLEAR_DEPTH, 1.0f, 0);
// 根据流水线类型设置渲染目标
switch (currentConfig.pipelineType)
{
case RenderPipelineType::Forward:
// 前向渲染:直接渲染到主渲染目标
deviceContext->OMSetRenderTargets(1, &mainRenderTargetView, depthStencilView);
break;
case RenderPipelineType::Deferred:
case RenderPipelineType::ForwardPlus:
// 延迟渲染:渲染到G-Buffer
{
ID3D11RenderTargetView* rtvs[4];
for (int i = 0; i < 4; ++i)
{
rtvs[i] = gBuffer[i].rtv;
// 清除G-Buffer
const float clearGBuffer[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
deviceContext->ClearRenderTargetView(rtvs[i], clearGBuffer);
}
deviceContext->OMSetRenderTargets(4, rtvs, depthStencilView);
}
break;
}
// 设置采样器
deviceContext->PSSetSamplers(0, 1, &linearSampler);
deviceContext->PSSetSamplers(1, 1, &pointSampler);
}
void RenderObject(
ID3D11Buffer* vertexBuffer,
ID3D11Buffer* indexBuffer,
UINT vertexCount,
UINT indexCount,
const XMMATRIX& worldMatrix,
const XMFLOAT4& materialColor,
float roughness = 0.5f,
float metalness = 0.0f)
{
// 更新每对象常量缓冲区
D3D11_MAPPED_SUBRESOURCE mapped;
deviceContext->Map(perObjectConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped);
PerObjectConstants* constants = static_cast<PerObjectConstants*>(mapped.pData);
XMStoreFloat4x4(&constants->worldMatrix, XMMatrixTranspose(worldMatrix));
constants->materialColor = materialColor;
constants->roughness = roughness;
constants->metalness = metalness;
deviceContext->Unmap(perObjectConstantBuffer, 0);
// 设置常量缓冲区
deviceContext->VSSetConstantBuffers(1, 1, &perObjectConstantBuffer);
deviceContext->PSSetConstantBuffers(1, 1, &perObjectConstantBuffer);
// 设置输入布局和顶点缓冲区
UINT stride = sizeof(Vertex);
UINT offset = 0;
deviceContext->IASetInputLayout(inputLayout);
deviceContext->IASetVertexBuffers(0, 1, &vertexBuffer, &stride, &offset);
// 设置索引缓冲区(如果有)
if (indexBuffer && indexCount > 0)
{
deviceContext->IASetIndexBuffer(indexBuffer, DXGI_FORMAT_R32_UINT, 0);
}
// 设置图元拓扑
deviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
// 根据流水线类型设置着色器
if (currentConfig.pipelineType == RenderPipelineType::Forward)
{
deviceContext->VSSetShader(forwardVS, nullptr, 0);
deviceContext->PSSetShader(forwardPS, nullptr, 0);
}
else
{
// 延迟渲染使用不同的着色器
deviceContext->VSSetShader(deferredVS, nullptr, 0);
deviceContext->PSSetShader(deferredPS, nullptr, 0);
}
// 绘制
if (indexBuffer && indexCount > 0)
{
deviceContext->DrawIndexed(indexCount, 0, 0);
}
else
{
deviceContext->Draw(vertexCount, 0);
}
}
void ApplyLightingPass()
{
if (currentConfig.pipelineType == RenderPipelineType::Deferred ||
currentConfig.pipelineType == RenderPipelineType::ForwardPlus)
{
// 延迟渲染的照明阶段
// 1. 将渲染目标切换回主渲染目标
deviceContext->OMSetRenderTargets(1, &mainRenderTargetView, nullptr);
// 2. 设置G-Buffer作为纹理输入
ID3D11ShaderResourceView* srvs[5];
srvs[0] = gBuffer[0].srv; // 漫反射+材质
srvs[1] = gBuffer[1].srv; // 法线+粗糙度
srvs[2] = gBuffer[2].srv; // 位置+金属度
srvs[3] = gBuffer[3].srv; // 自发光+其他
srvs[4] = depthSRV; // 深度
deviceContext->PSSetShaderResources(0, 5, srvs);
// 3. 设置照明着色器
deviceContext->VSSetShader(nullptr, nullptr, 0); // 全屏四边形不需要顶点着色器
deviceContext->PSSetShader(lightingPS, nullptr, 0);
// 4. 绘制全屏四边形
deviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
deviceContext->Draw(6, 0); // 6个顶点形成2个三角形
// 5. 解除纹理绑定
ID3D11ShaderResourceView* nullSRVs[5] = { nullptr, nullptr, nullptr, nullptr, nullptr };
deviceContext->PSSetShaderResources(0, 5, nullSRVs);
}
}
void ApplyPostProcessing()
{
// 这里可以实现各种后处理效果
if (currentConfig.enableSSAO)
{
ApplySSAO();
}
if (currentConfig.enableBloom)
{
ApplyBloom();
}
if (currentConfig.enableMotionBlur)
{
ApplyMotionBlur();
}
if (currentConfig.enableDepthOfField)
{
ApplyDepthOfField();
}
}
void ApplySSAO()
{
// 屏幕空间环境光遮蔽实现
// 在实际应用中,这通常需要单独的渲染目标和复杂的着色器
}
void ApplyBloom()
{
// 泛光效果实现
// 通常包括:亮度提取、高斯模糊、合成等步骤
}
void ApplyMotionBlur()
{
// 运动模糊实现
// 需要速度缓冲区记录像素运动
}
void ApplyDepthOfField()
{
// 景深效果实现
// 基于深度信息模糊背景和前景
}
void EndFrame()
{
// 后处理完成后,可以在这里执行最终的呈现
// 注意:实际的呈现应该在交换链中进行
}
void Resize(UINT newWidth, UINT newHeight)
{
// 更新视口
mainViewport.Width = static_cast<float>(newWidth);
mainViewport.Height = static_cast<float>(newHeight);
// 重新创建渲染目标
if (mainRenderTargetView) mainRenderTargetView->Release();
if (mainRenderTargetTexture) mainRenderTargetTexture->Release();
CreateMainRenderTarget(newWidth, newHeight);
// 重新创建深度缓冲区
if (depthStencilView) depthStencilView->Release();
if (depthStencilTexture) depthStencilTexture->Release();
if (depthSRV) depthSRV->Release();
CreateDepthStencil(newWidth, newHeight);
// 重新创建G-Buffer(如果存在)
if (!gBuffer.empty())
{
for (auto& buffer : gBuffer)
{
if (buffer.srv) buffer.srv->Release();
if (buffer.rtv) buffer.rtv->Release();
if (buffer.texture) buffer.texture->Release();
}
gBuffer.clear();
CreateGBuffer(newWidth, newHeight);
}
}
void Cleanup()
{
// 清理所有资源
if (inputLayout) inputLayout->Release();
if (forwardVS) forwardVS->Release();
if (forwardPS) forwardPS->Release();
if (deferredVS) deferredVS->Release();
if (deferredPS) deferredPS->Release();
if (lightingPS) lightingPS->Release();
if (perFrameConstantBuffer) perFrameConstantBuffer->Release();
if (perObjectConstantBuffer) perObjectConstantBuffer->Release();
if (linearSampler) linearSampler->Release();
if (pointSampler) pointSampler->Release();
if (depthSRV) depthSRV->Release();
if (depthStencilView) depthStencilView->Release();
if (depthStencilTexture) depthStencilTexture->Release();
if (mainRenderTargetView) mainRenderTargetView->Release();
if (mainRenderTargetTexture) mainRenderTargetTexture->Release();
for (auto& buffer : gBuffer)
{
if (buffer.srv) buffer.srv->Release();
if (buffer.rtv) buffer.rtv->Release();
if (buffer.texture) buffer.texture->Release();
}
gBuffer.clear();
}
// 常量缓冲区结构
struct PerFrameConstants
{
XMFLOAT4X4 viewMatrix;
XMFLOAT4X4 projectionMatrix;
XMFLOAT3 cameraPosition;
float padding;
};
struct PerObjectConstants
{
XMFLOAT4X4 worldMatrix;
XMFLOAT4 materialColor;
float roughness;
float metalness;
XMFLOAT2 paddingObj;
};
struct Vertex
{
XMFLOAT3 position;
XMFLOAT3 normal;
XMFLOAT2 texcoord;
};
~RenderPipelineManager()
{
Cleanup();
}
};
这个渲染流水线管理器实现了现代游戏引擎的核心功能:
13.3 高级渲染技术与优化
13.3.1 实例化渲染
实例化渲染是现代游戏中的重要优化技术,特别是在渲染大量相似对象时(如草地、树木、人群)。《刺客信条:英灵殿》中广阔的场景就大量使用了实例化渲染。
class InstancedRenderer
{
private:
struct InstanceData
{
XMFLOAT4X4 worldMatrix;
XMFLOAT4 color;
XMFLOAT2 uvOffset;
float scale;
float rotation;
};
ID3D11Device* device;
ID3D11DeviceContext* deviceContext;
// 实例缓冲区
ID3D11Buffer* instanceBuffer;
ID3D11Buffer* dynamicInstanceBuffer;
// 顶点和索引缓冲区
ID3D11Buffer* vertexBuffer;
ID3D11Buffer* indexBuffer;
// 着色器
ID3D11VertexShader* instancedVS;
ID3D11PixelShader* instancedPS;
ID3D11InputLayout* instancedInputLayout;
// 常量缓冲区
ID3D11Buffer* perFrameBuffer;
ID3D11Buffer* perInstanceBuffer;
// 实例数据
std::vector<InstanceData> instances;
UINT maxInstances;
UINT instanceStride;
public:
InstancedRenderer(ID3D11Device* dev, ID3D11DeviceContext* context)
: device(dev)
, deviceContext(context)
, instanceBuffer(nullptr)
, dynamicInstanceBuffer(nullptr)
, vertexBuffer(nullptr)
, indexBuffer(nullptr)
, instancedVS(nullptr)
, instancedPS(nullptr)
, instancedInputLayout(nullptr)
, perFrameBuffer(nullptr)
, perInstanceBuffer(nullptr)
, maxInstances(1000)
, instanceStride(sizeof(InstanceData))
{
}
bool Initialize(UINT maxInstanceCount = 1000)
{
maxInstances = maxInstanceCount;
// 创建实例缓冲区
D3D11_BUFFER_DESC instanceBufferDesc;
ZeroMemory(&instanceBufferDesc, sizeof(instanceBufferDesc));
instanceBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
instanceBufferDesc.ByteWidth = maxInstances * instanceStride;
instanceBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
instanceBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
instanceBufferDesc.MiscFlags = 0;
instanceBufferDesc.StructureByteStride = 0;
HRESULT result = device->CreateBuffer(&instanceBufferDesc, NULL, &instanceBuffer);
if (FAILED(result))
{
return false;
}
// 编译实例化着色器
if (!CompileInstancedShaders())
{
return false;
}
// 创建常量缓冲区
if (!CreateConstantBuffers())
{
return false;
}
return true;
}
bool CompileInstancedShaders()
{
// 实例化专用顶点着色器
const char* instancedVSCode =
"cbuffer PerFrameConstants : register(b0)\\n"
"{\\n"
" float4x4 viewProjMatrix;\\n"
" float3 cameraPosition;\\n"
" float time;\\n"
"};\\n"
"\\n"
"struct VertexInput\\n"
"{\\n"
" float3 position : POSITION;\\n"
" float3 normal : NORMAL;\\n"
" float2 texcoord : TEXCOORD0;\\n"
" float4 instanceColor : COLOR;\\n"
" float4x4 instanceWorld : WORLD;\\n"
" float2 instanceUVOffset : TEXCOORD1;\\n"
"};\\n"
"\\n"
"struct PixelInput\\n"
"{\\n"
" float4 position : SV_POSITION;\\n"
" float3 worldPosition : POSITION;\\n"
" float3 normal : NORMAL;\\n"
" float2 texcoord : TEXCOORD0;\\n"
" float4 color : COLOR;\\n"
"};\\n"
"\\n"
"PixelInput main(VertexInput input)\\n"
"{\\n"
" PixelInput output;\\n"
" \\n"
" // 应用实例的世界变换\\n"
" float4 worldPos = mul(float4(input.position, 1.0f), input.instanceWorld);\\n"
" \\n"
" // 应用视图投影变换\\n"
" output.position = mul(worldPos, viewProjMatrix);\\n"
" \\n"
" // 传递其他数据\\n"
" output.worldPosition = worldPos.xyz;\\n"
" output.normal = normalize(mul(input.normal, (float3x3)input.instanceWorld));\\n"
" output.texcoord = input.texcoord + input.instanceUVOffset;\\n"
" output.color = input.instanceColor;\\n"
" \\n"
" return output;\\n"
"}";
// 编译着色器…
// (为了简洁,省略了编译代码,与前面类似)
return true;
}
void UpdateInstances(const std::vector<InstanceData>& newInstances)
{
instances = newInstances;
// 确保不超过最大实例数
if (instances.size() > maxInstances)
{
instances.resize(maxInstances);
}
// 更新实例缓冲区
if (instances.empty())
{
return;
}
D3D11_MAPPED_SUBRESOURCE mapped;
deviceContext->Map(instanceBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped);
memcpy(mapped.pData, instances.data(), instances.size() * instanceStride);
deviceContext->Unmap(instanceBuffer, 0);
}
void Render()
{
if (instances.empty())
{
return;
}
// 设置顶点缓冲区(两个缓冲区:几何体数据和实例数据)
ID3D11Buffer* buffers[2] = { vertexBuffer, instanceBuffer };
UINT strides[2] = { sizeof(Vertex), instanceStride };
UINT offsets[2] = { 0, 0 };
deviceContext->IASetVertexBuffers(0, 2, buffers, strides, offsets);
deviceContext->IASetIndexBuffer(indexBuffer, DXGI_FORMAT_R32_UINT, 0);
// 设置输入布局
deviceContext->IASetInputLayout(instancedInputLayout);
// 设置图元拓扑
deviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
// 设置着色器
deviceContext->VSSetShader(instancedVS, nullptr, 0);
deviceContext->PSSetShader(instancedPS, nullptr, 0);
// 绘制实例化几何体
// 假设每个实例的三角形数量为triangleCount
UINT triangleCount = 100; // 示例值
deviceContext->DrawIndexedInstanced(triangleCount * 3, instances.size(), 0, 0, 0);
}
void Cleanup()
{
if (instanceBuffer) instanceBuffer->Release();
if (dynamicInstanceBuffer) dynamicInstanceBuffer->Release();
if (vertexBuffer) vertexBuffer->Release();
if (indexBuffer) indexBuffer->Release();
if (instancedVS) instancedVS->Release();
if (instancedPS) instancedPS->Release();
if (instancedInputLayout) instancedInputLayout->Release();
if (perFrameBuffer) perFrameBuffer->Release();
if (perInstanceBuffer) perInstanceBuffer->Release();
}
~InstancedRenderer()
{
Cleanup();
}
};
13.3.2 级联阴影映射
级联阴影映射是大型开放世界游戏中的关键技术,《上古卷轴5:天际》和《巫师3》都使用了这项技术来处理大范围的动态阴影。
class CascadedShadowMap
{
private:
struct CascadeData
{
XMFLOAT4X4 viewProjMatrix;
XMFLOAT4 splitDepth;
XMFLOAT3 lightDirection;
float padding;
};
ID3D11Device* device;
ID3D11DeviceContext* deviceContext;
// 阴影图纹理和视图
ID3D11Texture2D* shadowMapArray;
ID3D11DepthStencilView* shadowDSV;
ID3D11ShaderResourceView* shadowSRV;
// 阴影图尺寸和级联数量
UINT shadowMapSize;
UINT cascadeCount;
// 视锥分割距离
std::vector<float> cascadeSplits;
// 常量缓冲区
ID3D11Buffer* cascadeBuffer;
// 视口数组
std::vector<D3D11_VIEWPORT> cascadeViewports;
public:
CascadedShadowMap(ID3D11Device* dev, ID3D11DeviceContext* context)
: device(dev)
, deviceContext(context)
, shadowMapArray(nullptr)
, shadowDSV(nullptr)
, shadowSRV(nullptr)
, shadowMapSize(2048)
, cascadeCount(4)
, cascadeBuffer(nullptr)
{
// 默认级联分割(基于到摄像机的距离)
cascadeSplits = { 0.05f, 0.15f, 0.50f, 1.0f };
}
bool Initialize(UINT size = 2048, UINT cascades = 4)
{
shadowMapSize = size;
cascadeCount = cascades;
// 创建阴影图数组纹理
D3D11_TEXTURE2D_DESC shadowMapDesc;
ZeroMemory(&shadowMapDesc, sizeof(shadowMapDesc));
shadowMapDesc.Width = shadowMapSize;
shadowMapDesc.Height = shadowMapSize;
shadowMapDesc.MipLevels = 1;
shadowMapDesc.ArraySize = cascadeCount; // 每个级联一层
shadowMapDesc.Format = DXGI_FORMAT_R32_TYPELESS; // 用于深度和着色器采样
shadowMapDesc.SampleDesc.Count = 1;
shadowMapDesc.SampleDesc.Quality = 0;
shadowMapDesc.Usage = D3D11_USAGE_DEFAULT;
shadowMapDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL | D3D11_BIND_SHADER_RESOURCE;
shadowMapDesc.CPUAccessFlags = 0;
shadowMapDesc.MiscFlags = 0;
HRESULT result = device->CreateTexture2D(&shadowMapDesc, NULL, &shadowMapArray);
if (FAILED(result))
{
return false;
}
// 创建深度模板视图
D3D11_DEPTH_STENCIL_VIEW_DESC dsvDesc;
ZeroMemory(&dsvDesc, sizeof(dsvDesc));
dsvDesc.Format = DXGI_FORMAT_D32_FLOAT;
dsvDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DARRAY;
dsvDesc.Texture2DArray.MipSlice = 0;
dsvDesc.Texture2DArray.FirstArraySlice = 0;
dsvDesc.Texture2DArray.ArraySize = cascadeCount;
result = device->CreateDepthStencilView(shadowMapArray, &dsvDesc, &shadowDSV);
if (FAILED(result))
{
return false;
}
// 创建着色器资源视图
D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
ZeroMemory(&srvDesc, sizeof(srvDesc));
srvDesc.Format = DXGI_FORMAT_R32_FLOAT;
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY;
srvDesc.Texture2DArray.MostDetailedMip = 0;
srvDesc.Texture2DArray.MipLevels = 1;
srvDesc.Texture2DArray.FirstArraySlice = 0;
srvDesc.Texture2DArray.ArraySize = cascadeCount;
result = device->CreateShaderResourceView(shadowMapArray, &srvDesc, &shadowSRV);
if (FAILED(result))
{
return false;
}
// 为每个级联创建视口
for (UINT i = 0; i < cascadeCount; ++i)
{
D3D11_VIEWPORT viewport;
viewport.TopLeftX = 0.0f;
viewport.TopLeftY = 0.0f;
viewport.Width = static_cast<float>(shadowMapSize);
viewport.Height = static_cast<float>(shadowMapSize);
viewport.MinDepth = 0.0f;
viewport.MaxDepth = 1.0f;
cascadeViewports.push_back(viewport);
}
// 创建常量缓冲区
D3D11_BUFFER_DESC bufferDesc;
ZeroMemory(&bufferDesc, sizeof(bufferDesc));
bufferDesc.Usage = D3D11_USAGE_DYNAMIC;
bufferDesc.ByteWidth = sizeof(CascadeData) * cascadeCount;
bufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
bufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
result = device->CreateBuffer(&bufferDesc, NULL, &cascadeBuffer);
return SUCCEEDED(result);
}
void BeginShadowPass(UINT cascadeIndex)
{
if (cascadeIndex >= cascadeCount)
{
return;
}
// 设置当前级联的渲染目标
deviceContext->RSSetViewports(1, &cascadeViewports[cascadeIndex]);
// 清除深度缓冲区
deviceContext->ClearDepthStencilView(shadowDSV, D3D11_CLEAR_DEPTH, 1.0f, 0);
// 绑定阴影图为深度缓冲区
deviceContext->OMSetRenderTargets(0, nullptr, shadowDSV);
}
void CalculateCascadeMatrices(
const XMFLOAT3& cameraPosition,
const XMFLOAT3& cameraDirection,
const XMFLOAT3& lightDirection,
float cameraNear, float cameraFar,
float cameraFOV, float aspectRatio)
{
// 计算每个级联的视图投影矩阵
std::vector<CascadeData> cascadeData(cascadeCount);
for (UINT i = 0; i < cascadeCount; ++i)
{
float splitNear = (i == 0) ? cameraNear : cascadeSplits[i–1] * cameraFar;
float splitFar = cascadeSplits[i] * cameraFar;
// 计算级联的视锥
XMFLOAT4X4 cascadeViewProj = CalculateCascadeViewProj(
cameraPosition, cameraDirection, lightDirection,
splitNear, splitFar, cameraFOV, aspectRatio);
cascadeData[i].viewProjMatrix = cascadeViewProj;
cascadeData[i].splitDepth = XMFLOAT4(splitNear, splitFar, 0.0f, 0.0f);
cascadeData[i].lightDirection = lightDirection;
}
// 更新常量缓冲区
D3D11_MAPPED_SUBRESOURCE mapped;
deviceContext->Map(cascadeBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped);
memcpy(mapped.pData, cascadeData.data(), sizeof(CascadeData) * cascadeCount);
deviceContext->Unmap(cascadeBuffer, 0);
}
XMFLOAT4X4 CalculateCascadeViewProj(
const XMFLOAT3& cameraPos,
const XMFLOAT3& cameraDir,
const XMFLOAT3& lightDir,
float nearPlane, float farPlane,
float fov, float aspect)
{
// 计算摄像机视锥的角点
std::vector<XMFLOAT3> frustumCorners = CalculateFrustumCorners(
cameraPos, cameraDir, nearPlane, farPlane, fov, aspect);
// 计算包围这些点的AABB
XMFLOAT3 minBounds = frustumCorners[0];
XMFLOAT3 maxBounds = frustumCorners[0];
for (const auto& corner : frustumCorners)
{
minBounds.x = min(minBounds.x, corner.x);
minBounds.y = min(minBounds.y, corner.y);
minBounds.z = min(minBounds.z, corner.z);
maxBounds.x = max(maxBounds.x, corner.x);
maxBounds.y = max(maxBounds.y, corner.y);
maxBounds.z = max(maxBounds.z, corner.z);
}
// 从光源方向创建视图矩阵
XMVECTOR lightPos = XMVectorSet(
(minBounds.x + maxBounds.x) * 0.5f,
(minBounds.y + maxBounds.y) * 0.5f,
(minBounds.z + maxBounds.z) * 0.5f,
1.0f);
XMVECTOR lightTarget = XMVectorAdd(lightPos, XMLoadFloat3(&lightDir));
XMVECTOR lightUp = XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f);
XMMATRIX lightView = XMMatrixLookAtLH(lightPos, lightTarget, lightUp);
// 计算正交投影矩阵
XMFLOAT3 aabbMin, aabbMax;
TransformAABB(minBounds, maxBounds, lightView, aabbMin, aabbMax);
XMMATRIX lightProj = XMMatrixOrthographicOffCenterLH(
aabbMin.x, aabbMax.x,
aabbMin.y, aabbMax.y,
aabbMin.z, aabbMax.z);
// 组合视图投影矩阵
XMFLOAT4X4 result;
XMStoreFloat4x4(&result, XMMatrixMultiply(lightView, lightProj));
return result;
}
std::vector<XMFLOAT3> CalculateFrustumCorners(
const XMFLOAT3& cameraPos,
const XMFLOAT3& cameraDir,
float nearPlane, float farPlane,
float fov, float aspect)
{
std::vector<XMFLOAT3> corners(8);
// 计算近平面和远平面的半高和半宽
float nearHalfHeight = nearPlane * tan(fov * 0.5f);
float nearHalfWidth = nearHalfHeight * aspect;
float farHalfHeight = farPlane * tan(fov * 0.5f);
float farHalfWidth = farHalfHeight * aspect;
// 计算摄像机的右向量和上向量
XMVECTOR cameraRight = XMVectorSet(1.0f, 0.0f, 0.0f, 0.0f); // 简化:假设X轴为右
XMVECTOR cameraUp = XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f); // Y轴为上
XMVECTOR cameraForward = XMLoadFloat3(&cameraDir);
// 计算角点
// 近平面
corners[0] = cameraPos + cameraForward * nearPlane – cameraRight * nearHalfWidth – cameraUp * nearHalfHeight;
corners[1] = cameraPos + cameraForward * nearPlane + cameraRight * nearHalfWidth – cameraUp * nearHalfHeight;
corners[2] = cameraPos + cameraForward * nearPlane + cameraRight * nearHalfWidth + cameraUp * nearHalfHeight;
corners[3] = cameraPos + cameraForward * nearPlane – cameraRight * nearHalfWidth + cameraUp * nearHalfHeight;
// 远平面
corners[4] = cameraPos + cameraForward * farPlane – cameraRight * farHalfWidth – cameraUp * farHalfHeight;
corners[5] = cameraPos + cameraForward * farPlane + cameraRight * farHalfWidth – cameraUp * farHalfHeight;
corners[6] = cameraPos + cameraForward * farPlane + cameraRight * farHalfWidth + cameraUp * farHalfHeight;
corners[7] = cameraPos + cameraForward * farPlane – cameraRight * farHalfWidth + cameraUp * farHalfHeight;
return corners;
}
void TransformAABB(
const XMFLOAT3& minPt,
const XMFLOAT3& maxPt,
const XMMATRIX& transform,
XMFLOAT3& outMin,
XMFLOAT3& outMax)
{
// 变换AABB的所有角点
XMFLOAT3 corners[8] =
{
minPt,
XMFLOAT3(maxPt.x, minPt.y, minPt.z),
XMFLOAT3(minPt.x, maxPt.y, minPt.z),
XMFLOAT3(maxPt.x, maxPt.y, minPt.z),
XMFLOAT3(minPt.x, minPt.y, maxPt.z),
XMFLOAT3(maxPt.x, minPt.y, maxPt.z),
XMFLOAT3(minPt.x, maxPt.y, maxPt.z),
maxPt
};
// 初始化输出边界
XMVECTOR vMin = XMVectorSet(FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX);
XMVECTOR vMax = XMVectorSet(–FLT_MAX, –FLT_MAX, –FLT_MAX, –FLT_MAX);
// 变换所有点并更新边界
for (int i = 0; i < 8; ++i)
{
XMVECTOR corner = XMLoadFloat3(&corners[i]);
corner = XMVector3Transform(corner, transform);
vMin = XMVectorMin(vMin, corner);
vMax = XMVectorMax(vMax, corner);
}
XMStoreFloat3(&outMin, vMin);
XMStoreFloat3(&outMax, vMax);
}
void Cleanup()
{
if (shadowSRV) shadowSRV->Release();
if (shadowDSV) shadowDSV->Release();
if (shadowMapArray) shadowMapArray->Release();
if (cascadeBuffer) cascadeBuffer->Release();
}
ID3D11ShaderResourceView* GetShadowSRV() const { return shadowSRV; }
ID3D11Buffer* GetCascadeBuffer() const { return cascadeBuffer; }
UINT GetCascadeCount() const { return cascadeCount; }
~CascadedShadowMap()
{
Cleanup();
}
};
13.4 性能优化与调试
13.4.1 Direct3D调试技巧
在商业游戏开发中,调试图形问题是一项重要技能。以下是一些实用的调试技巧:
class Direct3DDebugHelper
{
private:
ID3D11Device* device;
ID3D11DeviceContext* deviceContext;
// 调试接口
ID3D11Debug* d3dDebug;
ID3D11InfoQueue* infoQueue;
public:
Direct3DDebugHelper(ID3D11Device* dev, ID3D11DeviceContext* context)
: device(dev)
, deviceContext(context)
, d3dDebug(nullptr)
, infoQueue(nullptr)
{
}
bool InitializeDebugLayer()
{
HRESULT result = device->QueryInterface(__uuidof(ID3D11Debug), (void**)&d3dDebug);
if (SUCCEEDED(result))
{
result = device->QueryInterface(__uuidof(ID3D11InfoQueue), (void**)&infoQueue);
if (SUCCEEDED(result))
{
// 设置信息队列过滤器
D3D11_INFO_QUEUE_FILTER filter;
ZeroMemory(&filter, sizeof(filter));
// 允许所有消息类型
D3D11_MESSAGE_SEVERITY severities[] =
{
D3D11_MESSAGE_SEVERITY_CORRUPTION,
D3D11_MESSAGE_SEVERITY_ERROR,
D3D11_MESSAGE_SEVERITY_WARNING,
D3D11_MESSAGE_SEVERITY_INFO,
D3D11_MESSAGE_SEVERITY_MESSAGE
};
filter.AllowList.NumSeverities = ARRAYSIZE(severities);
filter.AllowList.pSeverityList = severities;
// 设置存储限制
infoQueue->SetStorageFilterStackIndex(0);
infoQueue->AddStorageFilterEntries(&filter);
// 设置回调函数
infoQueue->SetMessageCountLimit(1000);
return true;
}
}
return false;
}
void CheckForLiveObjects()
{
if (d3dDebug)
{
OutputDebugString(L"=== 检查Direct3D存活对象 ===\\n");
d3dDebug->ReportLiveDeviceObjects(D3D11_RLDO_DETAIL | D3D11_RLDO_IGNORE_INTERNAL);
OutputDebugString(L"=== 检查完成 ===\\n");
}
}
void PrintMessages()
{
if (!infoQueue)
{
return;
}
UINT64 messageCount = infoQueue->GetNumStoredMessages();
for (UINT64 i = 0; i < messageCount; ++i)
{
SIZE_T messageSize = 0;
infoQueue->GetMessage(i, nullptr, &messageSize);
D3D11_MESSAGE* message = (D3D11_MESSAGE*)malloc(messageSize);
infoQueue->GetMessage(i, message, &messageSize);
// 根据严重级别输出消息
switch (message->Severity)
{
case D3D11_MESSAGE_SEVERITY_CORRUPTION:
OutputDebugString(L"[CORRUPTION] ");
break;
case D3D11_MESSAGE_SEVERITY_ERROR:
OutputDebugString(L"[ERROR] ");
break;
case D3D11_MESSAGE_SEVERITY_WARNING:
OutputDebugString(L"[WARNING] ");
break;
case D3D11_MESSAGE_SEVERITY_INFO:
OutputDebugString(L"[INFO] ");
break;
case D3D11_MESSAGE_SEVERITY_MESSAGE:
OutputDebugString(L"[MESSAGE] ");
break;
}
OutputDebugStringA(message->pDescription);
OutputDebugString(L"\\n");
free(message);
}
// 清空消息队列
infoQueue->ClearStoredMessages();
}
void BeginEvent(const std::wstring& eventName)
{
// 在GPU调试器中标记事件开始
// 注意:这需要Windows 8或更高版本
// 在实际代码中应使用条件编译
#ifdef _DEBUG
// deviceContext->BeginEvent(eventName.c_str());
#endif
}
void EndEvent()
{
#ifdef _DEBUG
// deviceContext->EndEvent();
#endif
}
void SetMarker(const std::wstring& markerName)
{
#ifdef _DEBUG
// deviceContext->SetMarker(markerName.c_str());
#endif
}
void Cleanup()
{
if (infoQueue) infoQueue->Release();
if (d3dDebug) d3dDebug->Release();
}
~Direct3DDebugHelper()
{
Cleanup();
}
};
13.4.2 性能分析
性能分析对于优化游戏至关重要。以下是一个简单的性能分析器:
class PerformanceProfiler
{
private:
struct ProfileSample
{
std::string name;
LARGE_INTEGER startTime;
LARGE_INTEGER endTime;
double duration;
UINT callCount;
double totalTime;
double minTime;
double maxTime;
};
LARGE_INTEGER frequency;
std::map<std::string, ProfileSample> samples;
public:
PerformanceProfiler()
{
QueryPerformanceFrequency(&frequency);
}
void BeginSample(const std::string& sampleName)
{
ProfileSample& sample = samples[sampleName];
sample.name = sampleName;
QueryPerformanceCounter(&sample.startTime);
}
void EndSample(const std::string& sampleName)
{
auto it = samples.find(sampleName);
if (it == samples.end())
{
return;
}
ProfileSample& sample = it->second;
QueryPerformanceCounter(&sample.endTime);
// 计算持续时间(毫秒)
sample.duration = static_cast<double>(sample.endTime.QuadPart – sample.startTime.QuadPart) * 1000.0 / frequency.QuadPart;
// 更新统计信息
sample.callCount++;
sample.totalTime += sample.duration;
if (sample.callCount == 1)
{
sample.minTime = sample.duration;
sample.maxTime = sample.duration;
}
else
{
sample.minTime = min(sample.minTime, sample.duration);
sample.maxTime = max(sample.maxTime, sample.duration);
}
}
void PrintResults()
{
OutputDebugString(L"=== 性能分析结果 ===\\n");
for (const auto& pair : samples)
{
const ProfileSample& sample = pair.second;
double avgTime = sample.totalTime / sample.callCount;
wchar_t buffer[512];
swprintf_s(buffer, L"%S: 调用次数=%u, 平均时间=%.3fms, 最小时间=%.3fms, 最大时间=%.3fms, 总时间=%.3fms\\n",
sample.name.c_str(),
sample.callCount,
avgTime,
sample.minTime,
sample.maxTime,
sample.totalTime);
OutputDebugString(buffer);
}
OutputDebugString(L"=== 分析结束 ===\\n");
// 重置统计信息
samples.clear();
}
double GetSampleDuration(const std::string& sampleName)
{
auto it = samples.find(sampleName);
if (it != samples.end())
{
return it->second.duration;
}
return 0.0;
}
};
13.5 总结
本章深入探讨了Direct3D核心编程的各个方面,从设备创建到高级渲染技术。我们学习了:
这些技术是商业游戏开发的基础。《赛博朋克2077》、《荒野大镖客2》等AAA游戏都建立在类似的架构之上。通过掌握这些核心概念,你已经为开发自己的Windows游戏奠定了坚实的基础。
记住,图形编程是一个不断发展的领域。DirectX 12已经引入了更底层的API控制,Vulkan也在跨平台领域占据重要地位。但无论API如何变化,本章讨论的核心概念——渲染流水线、资源管理、性能优化——都将是游戏开发中的重要基石。
在下一章中,我们将探讨更高级的主题,包括高级着色器技术、几何着色器、曲面细分和计算着色器。继续学习,你将成为一名真正的Windows游戏编程专家。
网硕互联帮助中心




评论前必须登录!
注册