一篇关于如何通过D3D12实现z-prepass技术的分享

Z-prepass的概念

在分享怎么实现之前,我们首先来看一下z-prepass到底是什么。
我们知道,在传统的图形管线中,被渲染的物体会在执行完像素着色器后执行深度检测,但这种方法有可能会在同一像素位置计算多个像素点,但实际显示在屏幕中的只有一个像素点,因此会产生大量的不必要的计算。
有一种优化策略是将深度检测的位置提前到像素着色器之前,即在执行完光栅化后便判断对应像素的深度,如果该像素的深度更小,则将其深度值写入深度缓冲区的对应位置,并对该像素进行像素着色过程。否则,便不会进行任何操作。这个过程被称为early-z。但是early-z依旧存在一些问题,比如当物体按照离屏幕由远及近的顺序渲染时,每一个物体的像素依旧会通过深度检测,因此并不会减少像素着色过程。
因此,z-prepass便是一种优化ealy-z的方法。z-prepass首先对需要被渲染的物体执行一遍渲染管线,但这个管线的像素着色器不执行任何操作,通过这种方法,借助深度检测机制,将离屏幕最近的物体的深度值写入深度缓冲区。然后执行第二遍渲染管线,这次的管线使用第一遍渲染产生的深度缓冲区,并利用early-z技术,使得只有与深度缓冲区中深度值相等的被渲染。

技术要点

z-prepass的所需要实现的技术要点如下:
1、 实现两个渲染管线,因此需要在Direct3D 12中创建两个pipeline state;
2、 需要创建两个CommandList以便于实现两个不同的渲染管线;
3、 需要实现两个像素着色器,其中一个用于z-prepass,另一个用于正常渲染;

具体实现

创建两个pipeline state

用于z-prepass的渲染管线需要打开深度检测,其代码如下

D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc_zprepass = {};
psoDesc_zprepass.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC(D3D12_DEFAULT);

用于正常渲染的渲染管线也需要进行一些修改,其代码如下

D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {};
psoDesc.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC(D3D12_DEFAULT);
psoDesc.DepthStencilState.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ZERO;
psoDesc.DepthStencilState.DepthFunc = D3D12_COMPARISON_FUNC_EQUAL;

其中,用于正常渲染的管线需要禁用深度缓冲区的写入,即将D3D12_GRAPHICS_PIPELINE_STATE_DESC中的DepthStencilState.DepthWriteMask置为D3D12_DEPTH_WRITE_MASK_ZERO。此外,需要将深度检测的判定条件修改为若像素深度值与深度缓冲区的深度值相等则通过深度检测。因此需要将DepthStencilState.DepthFunc置为D3D12_COMPARISON_FUNC_EQUAL

当然,上述的代码并不完整,但是pipeline state的其他状态像往常一样设置即可。

CommandList中需要注意的问题

由于需要两次渲染,因此每次渲染对应一个CommandList。
对于z-prepass的CommandList在绑定渲染目标时,可以只绑定深度缓冲区,其代码如下

prepassCommandList->ClearDepthStencilView(dsvHandle, D3D12_CLEAR_FLAG_DEPTH | D3D12_CLEAR_FLAG_STENCIL, 1.0f, 0, 0, nullptr);
prepassCommandList->OMSetRenderTargets(0, nullptr, true, &dsvHandle);

对于正常渲染的CommandList正常地绑定渲染目标即可,但是要注意,不要清除深度缓冲区,其代码如下

mCommandList->OMSetRenderTargets(1, &rtvHandle, true, &dsvHandle);

像素着色器中需要注意的问题

对于z-prepass的像素着色器,只需随意return一个float4的变量即可。
对于正常渲染的像素着色器而言,需要在像素着色器的声明之前添加[earlydepthstencil]属性,以表明GPU应该做early-z,虽然现在的大多数GPU默认使用early-z,但建议还是加上这个属性以防万一,其代码结构如下

[earlydepthstencil]
float4 PShader(VertexOut pin) : SV_Target
{
	// 你的程序逻辑 ...
}

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值