美文网首页
[unity]神秘的Early-Z到底是个啥?

[unity]神秘的Early-Z到底是个啥?

作者: 江枫枫Maple | 来源:发表于2019-04-24 20:32 被阅读0次

前言

      最近听到有朋友聊到了early-z,我们知道传统的渲染管线是:
      应用阶段(CPU)->几何阶段(顶点着色器)->光栅化阶段(片元着色器)->各种测试(深度测试,透明度测试,模板测试等)->颜色缓冲区(buffer)
      从上面的渲染流水线我们可以看出来,一系列测试是在执行过顶点/片元着色器后才执行的,也就是说就算我们指定了一些测试条件来剔除掉一部分像素,但是这些像素还是经过了计算,这部分计算是没有必要的。Over Draw就是这样产生的。

1.开始面向百度学习early-z

      文章(1)中提到:

现代GPU中运用了Early-Z的技术,在Vertex阶段和Fragment阶段之间(光栅化之后,fragment之前)进行一次深度测试,如果深度测试失败,就不必进行fragment阶段的计算了,因此在性能上会有很大的提升。但是最终的ZTest仍然需要进行,以保证最终的遮挡关系结果正确

      这里我们可以知道,early-z是放在片段着色器之前,所以大致的渲染管线可以描述成:
      应用阶段(CPU)->几何阶段(顶点着色器)->early-z(提前深度测试)->光栅化阶段(片元着色器)->各种测试(深度测试,透明度测试,模板测试等)->颜色缓冲区(buffer)
      提前进行深度测试可以节省不必要像素的片元计算,所以会有性能的提升。

Early-Z的实现,主要是通过一个Z-pre-pass实现,简单来说,对于所有不透明的物体(透明的没有用,本身不会写深度),首先用一个超级简单的shader进行渲染,这个shader不写颜色缓冲区,只写深度缓冲区,第二个pass关闭深度写入,开启深度测试,用正常的shader进行渲染。其实这种技术,我们也可以借鉴,在渲染透明物体时,因为关闭了深度写入,有时候会有其他不透明的部分遮挡住透明的部分,而我们其实不希望他们被遮挡,仅仅希望被遮挡的物体半透,这时我们就可以用两个pass来渲染,第一个pass使用Color Mask屏蔽颜色写入,仅写入深度,第二个pass正常渲染半透,关闭深度写入。

     我们怎么实现early-z呢?文中说的办法是先使用一个pass渲染深度,使用color mask屏蔽颜色写入,这种办法我们在解决透明物体自身遮挡关系的时候(详见《unity shader入门精要》172页,作者 : 冯乐乐)有使用到。
     但是毫无疑问,这样做会多出一个pass的消耗来,也就是说虽然这个pass里什么都没有计算,但是还是需要CPU告诉GPU顶点数据等信息。

只写入深度信息,但是还是占用了一个pass

2.Intel early-z相关文章

Intel的文章中提到:

This sample demonstrates two ways to take advantage of early Z rejection. When rendering, if the hardware detects that after performing vertex shading a fragment will fail the depth test, it can avoid the cost of executing the pixel shader and reject the fragment. To best take advantage of this feature, it is necessary to maximize the number of fragments that can be rejected because of depth testing. This sample demonstrates two ways of doing this:front to back rendering and z pre-pass.

     文章中提到,在执行过顶点着色器后,硬件会检查是否通过深度测试,这可以避免片段着色器的花费。文章中还展示了两种方式最大化利用硬件的early-z功能,一种是通过从前向后渲染,另外一种通过pass预传Z值实现。

2.1使用从前向后渲染

     我们知道在编写shaderlab的时候会为我们的shader指定渲染队列,这个渲染队列其实就是unity帮我们实现从前向后渲染功能的工具(根据渲染队列进行渲染,unity使用了正数索引代表渲染队列,索引号越小越早被渲染)。不透明物体,我们最好使用从前向后渲染,这样可以使用early-z功能,减少绘制的像素。而透明物体不可以使用这种early-z的方式,因为透明物体盖住的物体还是需要被渲染出来的,所以unity中不透明物体是从后往前渲染的,而且透明物体需要在不透明物体渲染完之后再进行渲染。所以unity已经帮我们实现了这种early-z的功能,我们只需要合理控制物体的渲染队列,就可以实现优化了。

2.2使用pass预传Z值渲染

     这种方式就是我们在第一章中提到的,使用一个额外的pass来写入深度值,但是屏蔽掉颜色写入。

For Z pre-pass, all opaque geometry is rendered in two passes. The first pass populates the Z buffer with depth values from all opaque geometry. A null pixel shader is used and the color buffer is not updated. For this first pass, only simple vertex shading is performed so unnecessary constant buffer updates and vertex-layout data should be avoided. For the second pass, the geometry is resubmitted with Z writes disabled but Z testing on and full vertex and pixel shading is performed. The graphics hardware takes advantage of Early-Z to avoid performing pixel shading on geometry that is not visible.

      文章中有提到第一个pass中只进行了简单的顶点着色,要避免不必要的常量缓冲区更新和顶点布局数据。也就是说这个渲染管线也与第一篇文章所描述的相同。early-z是在顶点着色器与片元着色器之间操作的。
      这里贴一下预传Z值的代码,代码很简单,只是开启了深度写入并屏蔽了颜色写入:

        Pass {
            ZWrite On
            ColorMask 0
        }

3.总结

      在Intel中文章的最后也提到说是图形硬件会根据Early-Z来避免不可见像素的计算。所以我个人理解是:early-z其实是基于硬件的一种优化方式,我们可以通过一些操作来使这种优化方式生效。
      因为我也是刚接触这方面,肯定有很多地方理解的不对,希望路过的大神可以帮忙指正QAQ

4.补

     通过其他文档中了解到透明度测试会导致early-z失效,所以在部分显卡上透明度测试会影响性能。
更多学习中的资料:
http://www.wolf96developer.cn/index.php/2018/12/14/%E5%85%B3%E4%BA%8Eearlyz/?tdsourcetag=s_pctim_aiomsg
https://zhuanlan.zhihu.com/p/53092784

5.额外的一些收获

     我们知道现在不透明物体开启了深度写入与深度测试后,会进行early-Z判断,但是这个时候还是需要应用层把相关的顶点数据传递给GPU,以进行深度测试。所以在FrameDebugger中,我们可以看到就算前面有一个很大的不透明物体,把后面的不透明物体都挡住了,但是后面的物体还是会占用drawcall,这个drawcall就是应用层把信息传递给GPU的数据。所以深度测试其实无法对CPU瓶颈进行优化,只是可以让没有通过深度测试的物体,不进行片元着色器的计算,从而减少计算量,降低GPU瓶颈。

相关文章

网友评论

      本文标题:[unity]神秘的Early-Z到底是个啥?

      本文链接:https://www.haomeiwen.com/subject/yrlggqtx.html