如何在DirectX11像素着色器中访问深度缓冲区纹理?
在DirectX11中实现像素着色器访问深度缓冲区
问题背景
我是着色器编程新手,若有知识疏漏或错误请见谅。目前正参考Nvidia《GPU Gems》中的运动模糊后期处理技术,该技术需将深度缓冲区作为纹理传入像素着色器以获取物体的世界空间位置。我使用Visual Studio的DirectX11通用C++模板创建项目,得到了初始的顶点着色器和像素着色器代码,但不清楚如何实现深度缓冲区值的访问。
初始顶点着色器代码:
cbuffer ModelViewProjectionConstantBuffer : register(b0) { matrix model; matrix view; matrix projection; }; struct VertexShaderInput { float3 pos : POSITION; float3 color : COLOR0; }; struct PixelShaderInput { float4 pos : SV_POSITION; float3 color : COLOR0; }; PixelShaderInput main(VertexShaderInput input) { PixelShaderInput output; float4 pos = float4(input.pos, 1.0f); pos = mul(pos, model); pos = mul(pos, view); pos = mul(pos, projection); output.pos = pos; output.color = input.color; return output; }
初始像素着色器代码:
struct PixelShaderInput { float4 pos : SV_POSITION; float3 color : COLOR0; }; float4 main(PixelShaderInput input) : SV_TARGET { return float4(input.color, 1.0f); }
Nvidia教程参考代码片段:
// 获取当前像素的深度缓冲区值 float zOverW = tex2D( depthTexture, texCoord); // H是当前像素在视口中的位置,范围-1到1 float4 H = float4(texCoord.x * 2 - 1, (1 - texCoord.y) * 2 - 1, zOverW, 1); // 通过视图-投影逆矩阵变换 float4 D = mul( H, g_ViewProjectionInverseMatrix); // 除以w得到世界空间位置 float4 worldPos = D / D.w;
实现步骤
1. 创建可作为纹理访问的深度缓冲区
默认DirectX11模板的深度缓冲区仅用于深度测试,无法直接绑定为纹理。需修改CreateDeviceResources方法,创建带着色器资源绑定属性的深度纹理:
// 替换原深度缓冲区创建逻辑 DXGI_FORMAT depthFormat = DXGI_FORMAT_D24_UNORM_S8_UINT; CD3D11_TEXTURE2D_DESC depthTexDesc(depthFormat, m_renderTargetSize.Width, m_renderTargetSize.Height, 1, 1, D3D11_BIND_DEPTH_STENCIL | D3D11_BIND_SHADER_RESOURCE); ComPtr<ID3D11Texture2D> depthTexture; DX::ThrowIfFailed( m_d3dDevice->CreateTexture2D(&depthTexDesc, nullptr, &depthTexture) ); // 创建深度模板视图(用于深度测试) CD3D11_DEPTH_STENCIL_VIEW_DESC dsvDesc(D3D11_DSV_TEXTURE2D); DX::ThrowIfFailed( m_d3dDevice->CreateDepthStencilView(depthTexture.Get(), &dsvDesc, &m_depthStencilView) ); // 创建着色器资源视图(用于像素着色器读取) CD3D11_SHADER_RESOURCE_VIEW_DESC srvDesc(D3D11_SRV_DIMENSION_TEXTURE2D, depthFormat); DX::ThrowIfFailed( m_d3dDevice->CreateShaderResourceView(depthTexture.Get(), &srvDesc, &m_depthTextureSRV) );
2. 修改着色器代码
顶点着色器(新增纹理坐标输出)
cbuffer ModelViewProjectionConstantBuffer : register(b0) { matrix model; matrix view; matrix projection; }; struct VertexShaderInput { float3 pos : POSITION; float3 color : COLOR0; }; struct PixelShaderInput { float4 pos : SV_POSITION; float3 color : COLOR0; float2 texCoord : TEXCOORD0; // 新增纹理坐标输出 }; PixelShaderInput main(VertexShaderInput input) { PixelShaderInput output; float4 pos = float4(input.pos, 1.0f); pos = mul(pos, model); pos = mul(pos, view); pos = mul(pos, projection); output.pos = pos; output.color = input.color; // 将裁剪空间坐标转换为[0,1]范围的UV output.texCoord = float2(pos.x / pos.w * 0.5f + 0.5f, 1.0f - (pos.y / pos.w * 0.5f + 0.5f)); return output; }
像素着色器(添加深度纹理访问逻辑)
struct PixelShaderInput { float4 pos : SV_POSITION; float3 color : COLOR0; float2 texCoord : TEXCOORD0; // 新增纹理坐标输入 }; Texture2D depthTexture : register(t0); SamplerState depthSampler : register(s0); cbuffer DepthConstantBuffer : register(b1) { matrix viewProjectionInverse; // 视图-投影矩阵的逆矩阵 }; float4 main(PixelShaderInput input) : SV_TARGET { // 读取深度值 float zOverW = depthTexture.Sample(depthSampler, input.texCoord).r; // 转换为NDC空间坐标(范围-1到1) float4 H = float4(input.texCoord.x * 2 - 1, (1 - input.texCoord.y) * 2 - 1, zOverW, 1.0f); // 计算世界空间位置 float4 D = mul(H, viewProjectionInverse); float4 worldPos = D / D.w; // 可替换为运动模糊逻辑,当前仅可视化深度值 return float4(zOverW, zOverW, zOverW, 1.0f); }
3. 渲染时绑定深度纹理与常量缓冲区
在Render方法中,绘制场景后添加以下代码:
// 计算并更新逆视图-投影矩阵 XMMATRIX viewProjection = XMLoadFloat4x4(&m_viewMatrix) * XMLoadFloat4x4(&m_projectionMatrix); XMMATRIX viewProjectionInverse = XMMatrixInverse(nullptr, viewProjection); XMFLOAT4X4 viewProjInv; XMStoreFloat4x4(&viewProjInv, viewProjectionInverse); DepthConstantBuffer depthCB; memcpy(&depthCB.viewProjectionInverse, &viewProjInv, sizeof(XMFLOAT4X4)); m_d3dContext->UpdateSubresource(m_depthConstantBuffer.Get(), 0, nullptr, &depthCB, 0, 0); // 绑定资源到像素着色器 m_d3dContext->PSSetConstantBuffers(1, 1, m_depthConstantBuffer.GetAddressOf()); m_d3dContext->PSSetShaderResources(0, 1, m_depthTextureSRV.GetAddressOf()); m_d3dContext->PSSetSamplers(0, 1, m_samplerState.GetAddressOf());
4. 验证效果
运行项目后,若场景显示为灰度渐变图像,说明深度值已成功读取,可在此基础上继续实现运动模糊逻辑。
内容的提问来源于stack exchange,提问作者Ryan Xu
相关产品推荐
相关产品推荐

