我在Windows 11上有一个DirectX 12桌面项目,它使用DXTK后处理效果的组合来实现后处理。
后处理序列的目的是最终得到单独的绽放和模糊纹理(以及在深度过程中渲染的深度纹理(,这些纹理在"大三角形"像素着色器中采样,以实现最终后台缓冲区屏幕图像的景深效果。
DXTK PostProcesses在全尺寸(1920x1080(屏幕纹理上运行。目前,这并没有影响性能(以60fps为基准(,但我想,当我最终想在未来支持4K分辨率时,这可能会成为一个问题,因为全尺寸图像后处理可能会很昂贵。
由于推荐的最佳实践是对源图像的按比例缩小的副本进行操作,我希望通过使用具有DownScale_2x2BasicPostProcess选项的半尺寸(即四分之一分辨率(工作纹理来实现这一点。但在尝试了几次这种效果后,只有原始源图像的左上角被渲染为缩小的纹理。。。不是文件中预期的完整图像:
缩小_2x2:将每个2x2像素块缩小到平均值。这是为了写入每个维度中大小为源纹理一半的渲染目标
其他注意事项:
- 场景几何体首先渲染到_R16G16B16_FLOATMSAA渲染目标,并解析为单个采样16fp目标
- 后处理对已解析的单个样本16fp目标进行操作(其中只有中间"Pass1"one_answers"Pass2"工作渲染目标设置为后缓冲区长度和宽度的一半(
- 最终处理的图像被色调映射到_R10G10B10A2_UNORM交换链后缓冲区以用于呈现
以下代码片段显示了如何在后期处理中实现DownScale_2x2着色器。希望这足以解决这个问题,如果有必要,我可以更新更多信息。
CreateDeviceDependentResources((下的资源初始化:
namespace GameConstants {
constexpr DXGI_FORMAT BACKBUFFERFORMAT(DXGI_FORMAT_R10G10B10A2_UNORM); // back buffer to support hdr rendering
constexpr DXGI_FORMAT HDRFORMAT(DXGI_FORMAT_R16G16B16A16_FLOAT); // format for hdr render targets
constexpr DXGI_FORMAT DEPTHFORMAT(DXGI_FORMAT_D32_FLOAT); // format for render target depth buffer
constexpr UINT MSAACOUNT(4u); // requested multisample count
}
...
//
// Render targets
//
mMsaaHelper = std::make_unique<MSAAHelper>(GameConstants::HDRFORMAT, GameConstants::DEPTHFORMAT, GameConstants::MSAACOUNT);
mMsaaHelper->SetClearColor(GameConstants::CLEARCOLOR);
mDistortionRenderTex = std::make_unique<RenderTexture>(GameConstants::BACKBUFFERFORMAT);
mHdrRenderTex = std::make_unique<RenderTexture>(GameConstants::HDRFORMAT);
mPass1RenderTex = std::make_unique<RenderTexture>(GameConstants::HDRFORMAT);
mPass2RenderTex = std::make_unique<RenderTexture>(GameConstants::HDRFORMAT);
mBloomRenderTex = std::make_unique<RenderTexture>(GameConstants::HDRFORMAT);
mBlurRenderTex = std::make_unique<RenderTexture>(GameConstants::HDRFORMAT);
mDistortionRenderTex->SetClearColor(GameConstants::CLEARCOLOR);
mHdrRenderTex->SetClearColor(GameConstants::CLEARCOLOR);
mPass1RenderTex->SetClearColor(GameConstants::CLEARCOLOR);
mPass2RenderTex->SetClearColor(GameConstants::CLEARCOLOR);
mBloomRenderTex->SetClearColor(GameConstants::CLEARCOLOR);
mBlurRenderTex->SetClearColor(GameConstants::CLEARCOLOR);
mMsaaHelper->SetDevice(device); // Set the MSAA device. Note this updates GetSampleCount.
mDistortionRenderTex->SetDevice(device,
mPostProcSrvDescHeap->GetCpuHandle(SRV_PostProcDescriptors::DistortionMaskSRV),
mRtvDescHeap->GetCpuHandle(RTV_Descriptors::DistortionMaskRTV));
mHdrRenderTex->SetDevice(device,
mPostProcSrvDescHeap->GetCpuHandle(SRV_PostProcDescriptors::HdrSRV),
mRtvDescHeap->GetCpuHandle(RTV_Descriptors::HdrRTV));
mPass1RenderTex->SetDevice(device,
mPostProcSrvDescHeap->GetCpuHandle(SRV_PostProcDescriptors::Pass1SRV),
mRtvDescHeap->GetCpuHandle(RTV_Descriptors::Pass1RTV));
mPass2RenderTex->SetDevice(device,
mPostProcSrvDescHeap->GetCpuHandle(SRV_PostProcDescriptors::Pass2SRV),
mRtvDescHeap->GetCpuHandle(RTV_Descriptors::Pass2RTV));
mBloomRenderTex->SetDevice(device,
mPostProcSrvDescHeap->GetCpuHandle(SRV_PostProcDescriptors::BloomSRV),
mRtvDescHeap->GetCpuHandle(RTV_Descriptors::BloomRTV));
mBlurRenderTex->SetDevice(device,
mPostProcSrvDescHeap->GetCpuHandle(SRV_PostProcDescriptors::BlurSRV),
mRtvDescHeap->GetCpuHandle(RTV_Descriptors::BlurRTV));
...
RenderTargetState ppState(GameConstants::HDRFORMAT, DXGI_FORMAT_UNKNOWN); // 2d postproc rendering
...
// Set other postprocessing effects
mBloomExtract = std::make_unique<BasicPostProcess>(device, ppState, BasicPostProcess::BloomExtract);
mBloomPass = std::make_unique<BasicPostProcess>(device, ppState, BasicPostProcess::BloomBlur);
mBloomCombine = std::make_unique<DualPostProcess>(device, ppState, DualPostProcess::BloomCombine);
mGaussBlurPass = std::make_unique<BasicPostProcess>(device, ppState, BasicPostProcess::GaussianBlur_5x5);
mDownScalePass = std::make_unique<BasicPostProcess>(device, ppState, BasicPostProcess::DownScale_2x2);
在CreateWindowSizeDependentResources((下调整资源大小:
// Get current backbuffer dimensions
CD3DX12_RECT outputRect(mDeviceResources->GetOutputSize());
// Determine the render target size in pixels
mBackbufferSize.x = std::max<UINT>(outputRect.right - outputRect.left, 1u);
mBackbufferSize.y = std::max<UINT>(outputRect.bottom - outputRect.top, 1u);
...
mMsaaHelper->SetWindow(outputRect);
XMUINT2 halfSize(mBackbufferSize.x / 2u, mBackbufferSize.y / 2u);
mBloomRenderTex->SetWindow(outputRect);
mBlurRenderTex->SetWindow(outputRect);
mDistortionRenderTex->SetWindow(outputRect);
mHdrRenderTex->SetWindow(outputRect);
mPass1RenderTex->SizeResources(halfSize.x, halfSize.y);
mPass2RenderTex->SizeResources(halfSize.x, halfSize.y);
后处理实施:
mMsaaHelper->Prepare(commandList);
Clear(commandList);
// Render 3d scene
mMsaaHelper->Resolve(commandList, mHdrRenderTex->GetResource(),
D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_RENDER_TARGET);
//
// Postprocessing
//
// Set texture descriptor heap in prep for postprocessing if necessary.
// Unbind dsv for postprocess textures and sprites.
ID3D12DescriptorHeap* postProcHeap[] = { mPostProcSrvDescHeap->Heap() };
commandList->SetDescriptorHeaps(UINT(std::size(postProcHeap)), postProcHeap);
// downscale pass
CD3DX12_CPU_DESCRIPTOR_HANDLE rtvDownScaleDescriptor(mRtvDescHeap->GetCpuHandle(RTV_Descriptors::Pass1RTV));
commandList->OMSetRenderTargets(1u, &rtvDownScaleDescriptor, FALSE, nullptr);
mPass1RenderTex->BeginScene(commandList); // transition to render target state
mDownScalePass->SetSourceTexture(mPostProcSrvDescHeap->GetGpuHandle(SRV_PostProcDescriptors::HdrSRV), mHdrRenderTex->GetResource());
mDownScalePass->Process(commandList);
mPass1RenderTex->EndScene(commandList); // transition to pixel shader resource state
// blur horizontal pass
commandList->OMSetRenderTargets(1u, &rtvPass2Descriptor, FALSE, nullptr);
mPass2RenderTex->BeginScene(commandList); // transition to render target state
mGaussBlurPass->SetSourceTexture(mPostProcSrvDescHeap->GetGpuHandle(SRV_PostProcDescriptors::Pass1SRV), mPass1RenderTex->GetResource());
//mGaussBlurPass->SetSourceTexture(mPostProcSrvDescHeap->GetGpuHandle(SRV_PostProcDescriptors::HdrSRV), mHdrRenderTex->GetResource());
mGaussBlurPass->SetGaussianParameter(1.f);
mGaussBlurPass->SetBloomBlurParameters(TRUE, 4.f, 1.f); // horizontal blur
mGaussBlurPass->Process(commandList);
mPass2RenderTex->EndScene(commandList); // transition to pixel shader resource
// blur vertical pass
CD3DX12_CPU_DESCRIPTOR_HANDLE rtvBlurDescriptor(mRtvDescHeap->GetCpuHandle(RTV_Descriptors::BlurRTV));
commandList->OMSetRenderTargets(1u, &rtvBlurDescriptor, FALSE, nullptr);
mBlurRenderTex->BeginScene(commandList); // transition to render target state
mGaussBlurPass->SetSourceTexture(mPostProcSrvDescHeap->GetGpuHandle(SRV_PostProcDescriptors::Pass2SRV), mPass2RenderTex->GetResource());
mGaussBlurPass->SetBloomBlurParameters(FALSE, 4.f, 1.f); // vertical blur
mGaussBlurPass->Process(commandList);
mBlurRenderTex->EndScene(commandList); // transition to pixel shader resource
// render the final image to hdr texture
CD3DX12_CPU_DESCRIPTOR_HANDLE rtvHdrDescriptor(mRtvDescHeap->GetCpuHandle(RTV_Descriptors::HdrRTV));
commandList->OMSetRenderTargets(1u, &rtvHdrDescriptor, FALSE, nullptr);
//mHdrRenderTex->BeginScene(commandList); // transition to render target state
commandList->SetGraphicsRootSignature(mRootSig.Get()); // bind root signature
commandList->SetPipelineState(mPsoDepthOfField.Get()); // set PSO
commandList->SetGraphicsRootConstantBufferView(RootParameterIndex::PSDofCB, psDofCB.GpuAddress());
commandList->SetGraphicsRootDescriptorTable(RootParameterIndex::PostProcDT, mPostProcSrvDescHeap->GetFirstGpuHandle());
// use the big triangle optimization to draw a fullscreen quad
commandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
commandList->DrawInstanced(3u, 1u, 0u, 0u);
...
PIXBeginEvent(commandList, PIX_COLOR_DEFAULT, L"Tone Map");
// Set swapchain backbuffer as the tonemapping render target and unbind depth/stencil for sprites (UI)
CD3DX12_CPU_DESCRIPTOR_HANDLE rtvDescriptor(mDeviceResources->GetRenderTargetView());
commandList->OMSetRenderTargets(1u, &rtvDescriptor, FALSE, nullptr);
CD3DX12_GPU_DESCRIPTOR_HANDLE postProcTexture(mPostProcSrvDescHeap->GetGpuHandle(SRV_PostProcDescriptors::HdrSRV));
ApplyToneMapping(commandList, postProcTexture);
顶点着色器:
/*
We use the 'big triangle' optimization that only requires three vertices to completely
cover the full screen area.
v0 v1 ID NDC UV
*____* -- ------- ----
| | / 0 (-1,+1) (0,0)
|_|/ 1 (+3,+1) (2,0)
| / 2 (-1,-3) (0,2)
|/
*
v2
*/
TexCoordVertexOut VS(uint id : SV_VertexID)
{
TexCoordVertexOut vout;
vout.texCoord = float2((id << 1u) & 2u, id & 2u);
// See Luna p.687
float x = vout.texCoord.x * 2.f - 1.f;
float y = -vout.texCoord.y * 2.f + 1.f;
// Procedurally generate each NDC vertex.
// The big triangle produces a quad covering the screen in NDC space.
vout.posH = float4(x, y, 0.f, 1.f);
// Transform quad corners to view space near plane.
float4 ph = mul(vout.posH, InvProj);
vout.posV = ph.xyz / ph.w;
return vout;
}
像素着色器:
float4 PS(TexCoordVertexOut pin) : SV_TARGET
//float4 PS(float2 texCoord : TEXCOORD0) : SV_TARGET
{
...
// Get downscale texture sample
float3 colorDownScale = Pass1Tex.Sample(PointSampler, pin.texCoord).rgb;
...
return float4(colorDownScale, 1.f); // only top-quarter of source input is rendered!
//return float4(colorOutput, 1.f);
//return float4(distortCoords, 0.f, 1.f);
//return float4(colorHDR, 1.f);
//return float4(colorBlurred, 1.f);
//return float4(colorBloom, 1.f);
//return float4((p.z * 0.01f).rrr, 1.f); // multiply by a contrast factor
}
PostProcess
类使用"全屏四边形"渲染模型。由于我们可以依赖Direct3D10.0或更高级别的硬件,因此它使用"自生成四边形"模型来避免对VB.的需要
因此,自生成四边形将定位在视口设置的任何位置。剪刀设置也是需要的,因为它使用"剪刀";"大三角形";优化以避免在图像上出现对角线接缝如果视口位于除完整渲染目标之外的任何位置。
我在编写自定义着色器教程中有这个细节,但我忘了在wiki上的PostProcess文档中复制它。
TL;DR:当您要渲染到较小的渲染目标时,请使用:
auto vp = m_deviceResources->GetScreenViewport();
Viewport halfvp(vp);
halfvp.height /= 2.f;
halfvp.width /= 2.f;
commandList->RSSetViewports(1, halfvp.Get12());
然后,当我们切换回您的全尺寸渲染目标时,使用:
commandList->RSSetViewports(1, &vp);
已更新wiki页面。