URP DepthPrepass探索 & Shader不显示问题分析
由问题“URP 下通过右键菜单创建的 Shader 不显示任何内容”发散而来,引申出对 URP DepthPrepass 的讨论。
上述问题原因
URP 默认 Pipeline 下,渲染不透明物体前,如有下述情况之一,会先执行深度写入 Pass:
- 有
RenderFeature
需要Depth Texture
/Normal Texture
- 相机选中
DepthTexture
(强制开启或 Pipeline Asset 勾选 DepthTexture) DepthPrimingMode && Forward Rendering && RenderType Base && CameraType != Reflection
Unity 把这个过程叫做 DepthPrepass。
接下来,渲染不透明物体时:
- 未开启
Depth Priming Mode
:- 如果没有特别声明,采用
ZTest LessEqual
的方式剔除片元 - 不利用
Depth Texture
- 如果没有特别声明,采用
- 开启
Depth Priming Mode
:- 默认使用
ZTest Equal
,结合Depth Texture
来剔除片元 - 对不透明队列 Overdraw 的优化
- 默认使用
如果不透明物体的 Shader 不包含 DepthPrepass 指定的 Pass,那 Prepass 不会写入当前物体的深度。
由于没有写深度,DepthPrimingMode
下片元深度始终不 Equal 深度图,片元自然就被剔除了。
新版 URP 默认勾选了DepthPrimingMode
,因此右键创建的 Shader 文件渲染不显示任何内容。
指定的 Pass 及调用时机
URP 默认 Pipeline 下,使用 Pass Tag 名硬编码了两组 Pass:
- DepthOnlyPass:
DepthOnly
- DepthNormalOnlyPass:
DepthNormals
/DepthNormalsOnly
默认 Pipeline 中,选择 DepthOnlyPass 还是 DepthNormalOnlyPass 的逻辑如下:
if(requiresDepthPrepass)
{
if(renderPassInputs.requiresNormalsTexture)
{
if(this.actualRenderingMode == RenderingMode.Deferred)
{
//其他内容略...
m_DepthNormalPrepass.Setup(xxx);
}else
{
m_DepthNormalPrepass.Setup(xxx);
}
EnqueuePass(m_DepthNormalPrepass);
}else
{
if(this.actualRenderingMode != RenderingMode.Deferred)
{
m_DepthPrepass.Setup(xxx);
EnqueuePass(m_DepthPrepass);
}
}
}
根据 URP 源码可知,当 RenderFeature(如 SSAO)启用了 Normal 时,会执行 DepthNormalOnlyPass,否则执行 DepthOnlyPass。
有什么用
上面已经说到了,该举是为了优化不透明队列的 OverDraw 问题。
虽然我们期望不透明队列从前往后渲染,但这个排序是对 Mesh 的大致排序,而不是针对图片或片元的排序(CPU 端不可能去逐图元或片元处理),OverDraw 总会存在。
使用 ZTest Equal 结合深度图,不透明队列的 Overdraw 会得到明显优化。
开头问题的解决方法
无所谓做法
关掉Depth Priming Mode
,反正Depth Priming Mode
+ MSAA
在 TBR GPU 上的收益不一定大。
稳妥的方法
不透明物体老老实实补全相关的 Pass,深度 Pass 只为写深度,内容不要太复杂。
如果没有顶点或片元位移,照搬 URP Lit 的 Pass 就行。