【笔记】【百人计划】图形3.5 Early-z和Z-prepass(preZ)

一、深度测试

  • fs-Alpha Test-Stencil Test-==Depth Test==

  • 解决物体可见遮挡性的问题

image-20220822155007188

因为测试阶段发生在fs之后,片元即便被丢弃,已经经过了fs中大量的无用的计算。

二、提前深度测试Early-Z

  • 解决过多不必要的片元计算问题
  • 光栅化-==EarlyZ==-fs-Alpha Test-Stencil Test-==Depth Test==
image-20220822155402937
  • EarlyZ也可以基于模板测试在着色前丢弃片元
  • EarlyZ剔除的最小单位不是1像素,而是像素块(2x2)

2.1 EarlyZ失效

通常在EarlyZ阶段不仅会进行深度测试,还会写入深度

  1. 开启ALpha Test或Clip/discard等手动丢弃片元操作
    • 如果手动丢弃片元,导致如果前面的片元丢弃了,深度仍然存在,后续的像素也会被丢弃。
  2. 手动修改GPU插值得到的深度
  3. 开启Alpha Blend
    • 开启Alpha Blend的物体一般不会开启深度写入ZWrite off
  4. 关闭深度测试Depth Test
  5. 开启Multi-Sampling:多重采样会影响周边像素,而EarlyZ无法得知周边像素是否被裁剪,无法提前剔除
  6. 以及其他任何导致需要混合后面颜色的操作

此外,Early-Z技术会导致**深度数据冲突(depth data hazard)**的问题

img

避免深度数据冲突的方法之一是在写入深度值之前,再次与frame buffer的值进行对比:

1617944-20190906001545523-562795391.png

2.2 高效利用EarlyZ

将不透明物体由近向远渲染,EarlyZ的优化效果最好

  • CPU进行深度排序
  • 场景复杂时,排序消耗增大。
  • 严格按照从近到远渲染,不能同时搭配合批优化手段
image-20220822155921813

三、使用Z-Prepass

  1. 在第一个pass即Z-Prepass中只写入深度,不计算输出任何颜色
    • 自动计算了最小深度的zbuffer,无需cpu排序
    • EarlyZ阶段也可以提升一点效率,虽然本来就没什么计算
  2. 第二个pass关闭深度写入,并且将深度比较函数设置为相等
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
SubShader {
Tags{"RenderType" = "Opaque"}
//PreZ
Pass {
ZWrite On // 开启深度写入
ColorMask 0 // 关闭颜色输出
CGROGRAM
...//省略顶点变换部分
ENDCG
}
//正常地计算输出颜色
Pass {
ZWrite Off // 关闭深度写入
ZTest Equal // 深度相等通过
CGPROGRAM
...
ENDCG
}
}

四、Z-Prepass带来的问题

4.1 动态批处理问题

多pass的shader无法进行动态批处理-增加drawcall

解决方法:提前分离的PrePass

仍然使用两个pass

  1. 将原先第一个pass(Z-Prepass)单独分离出来为单独一个shader,并使用这个shader将整个场景的Opaque物体渲染一遍(写入深度)
  2. 原先材质只剩下原先的第二个Pass,仍关闭深度写入,并且深度比较函数设置为相等

两个pass都能够分别被批处理

4.2 Z-Prepass解决透明渲染

不写入深度的透明渲染会出现深度穿插错误的问题

image-20220822161301143(1)

(无法看到透明物体的背面)

  • 如果要用Z-Prepass同时渲染物体背面
    • 需要先渲染背面,剔除正面
    • 在下一个pass渲染正面,剔除背面
    • 可用于头发渲染

4.3 其他问题

计算消耗

image-20220822161643680

权衡片元计算复杂性/overdraw与Z-Prepass的消耗,根据实际情况采用

五、Early-Z与Z-Prepass的实例应用

https://www.cnblogs.com/jaffhan/p/7382106.html

image-20220822161834083 image-20220822161855886

作业

  1. 做下preZ的效果测试

左边的狮子是preZ,右边的狮子是普通的Alpha。

image-20220823202352241

因为是透明渲染,所以自然而然会想到preZ的方法能否应用到之前深度测试的透视效果。

因为preZ记录了最前面的一个深度。透视,也就是说透明物体前面被遮挡住了,并且已经绘制了这个遮挡物体,那么就是遮挡物体的深度写入了深度缓冲。我们也就没有了被遮挡物体最近面的深度了。而之后要绘制出来,也要用Greater的判定方式,最终透明物体无法表现出自遮挡。

并且如果没有遮挡物的时候,物体自身最近片元就写入了深度。那么物体自己对自己形成遮挡,最后会发生没有遮挡时,绘制出了物体自身重叠部分。这种没有对透视效果做特殊处理,所以最后一次绘制物体自身的时候会覆盖(这是深度缓冲依然是物体自身最近距离,测试规则为LEqual)但是如果透视部分有特殊效果,那么正常的物体渲染可能会漏出这一部分。

总之结论就是,这个效果同一物体多pass的实现方法,无法用preZ来完成。

image-20220823203258886(1)

  1. 总结earlyZ的限制
  • EarlyZ-失效

    • 开启ALpha Test或Clip/discard等手动丢弃片元操作
      • 如果手动丢弃片元,导致如果前面的片元丢弃了,深度仍然存在,后续的像素也会被丢弃。
    • 手动修改GPU插值得到的深度
    • 开启Alpha Blend
      • 开启Alpha Blend的物体一般不会开启深度写入ZWrite off
    • 关闭深度测试Depth Test
    • 开启Multi-Sampling:多重采样会影响周边像素,而EarlyZ无法得知周边像素是否被裁剪,无法提前剔除
    • 以及其他任何导致需要混合后面颜色的操作
  • 导致深度数据冲突

  • 为了最大利用EarlyZ按照从近到远顺序渲染,但CPU阶段排序耗时,且严格按照从近到远渲染不能同时搭配合批优化手段

参考资料

[1] https://www.bilibili.com/video/BV1FK4y1u7iw

【技术美术百人计划】图形 3.5 Early-z和Z-prepass

[2] https://www.bilibili.com/video/BV1aM4y1g75f

【技术美术百人计划】图形 2.7.2 GPU硬件架构概述

[3] https://www.cnblogs.com/timlly/p/11471507.html