深度测试Depth Test
Z-Test、Z-Buffer、Early - z
综合笔记,源见参考
1 什么是深度测试
所谓深度测试,就是针对当前对象在屏幕上(更准确的说是frame buffer)对应的像素点,将对象自身的深度值与当前该像素点缓存的深度值进行比较,如果通过了,本对象在该像素点才会将颜色写入颜色缓冲区,否则否则不会写入颜色缓冲。
顶点>> 片元 >> 透明 >> Stencil Test >> Depth Test >> 混合 - - >输出
1.1 深度缓冲(Z-Buffer)
深度缓冲就像颜色缓冲(Color Buffer)(储存所有的片段颜色:视觉输出)一样,在每个片段中储存了信息,并且(通常)和颜色缓冲有着一样的宽度和高度。
深度缓冲是由窗口系统自动创建的,它会以16、24或32位float的形式储存它的深度值。在大部分的系统中,深度缓冲的精度都是24位的。
深度缓冲区中存储的深度值为0到1范围的浮点值,且为非线性,正确的投影特性的非线性深度方程是和1/z成正比的 ,这样基本上做的是在z很近是的高精度和 z 很远的时候的低精度。
1.2 通俗的理解深度缓冲区、深度测试、深度写入
深度测试存在的目的很大程度是为了避免overdraw问题,在绘画过程中覆盖掉上一层的行为在计算机中是非常消耗的做法,避免渲染被遮挡的像素,我们可以对不同的物体进行排序,但是当物体间存在相互遮挡关系时,排列物体就不能解决问题。
深度缓冲区正是为此而产生的,排列物体不可行,但排列像素是可行的,用一个深度缓冲区(z-buffer)把场景中每个像素深度保存下来,之后还涉及深度测试(z-test)和深度写入(z-write),我们用这算法能够比较好地处理物体遮挡关系。
2 Z-buffer算法
我们这里假设我们摄像机在原点看向 z 正方向,即离我们越远z越大,深度越大,离我们越近z越小深度越小。
简单来说,我们初始化深度缓存为无限大,每个像素只记录离该像素最小的深度,如果新插入的像素(物体)深度比目前小(深度测试),则更新该值(深度写入),并将新的像素值写入颜色缓冲区。
深度测试分为通过和不通过两种情况,深度写入分为开启和不开启两种,那么就一共有是四种情况:
- 深度测试通过,深度写入开启→更新深度缓冲区,更新颜色缓冲区
- 深度测试不通过,深度写入开启→不更新深度缓冲区,不更新颜色缓冲区
- 深度测试通过,深度写入不开启→不更新深度缓冲区,更新颜色缓冲区
- 深度测试不通过,深度写入不开启→不更新深度缓冲区,不更新颜色缓冲区
深度写入的两种状态
- ZWrite On与ZWrite Off
当我们开启深度写入的时候,物体被渲染时针对物体在屏幕(更准确地说是frame buffer)上每个像素的深度都写入到深度缓冲区;反之,如果是ZWrite Off,那么物体的深度就不会写入深度缓冲区。
但是,物体是否会写入深度,除了ZWrite这个状态之外,更重要的是需要深度测试通过,也就是ZTest通过,如果ZTest都没通过,那么也就不会写入深度了。
3 在Unity中编写
在ShaderLab中使用ZTest param
来设定深度测试的参数, 可以给SubShader, 也可以给Pass设置, 使用ZWrite Off/On
来开关深度写入,默认是ZWrite On。
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
ZTest Always
Pass
{
ZTest Greater
ZWrite On
}
}
Z Test 对比状态如下:
默认的参数是LEqual
, 即源深度值小于等于目标深度值时通过测试。需要注意的是,因为深度缓存的默认是无限大,所以直接使用Greater,所有片元都会被抛弃。
4 Early Z
现在大多数 GPU 都支持一种称为提前深度测试(Early depth testing)的硬件功能。提前深度测试允许深度测试在片段着色器之前运行。明确一个片段永远不会可见的 (它是其它物体的后面) 我们可以更早地放弃该片段。
片段着色器通常是相当费时的所以我们应该尽量避免运行它们。对片段着色器提前深度测试一个限制是,你不应该写入片段的深度值。如果片段着色器将写入其深度值,提前深度测试是不可能的,OpenGL不能事先知道深度值。
传统的渲染管线中,ZTest其实是在Blending阶段,这时候进行深度测试,所有对象的像素着色器都会计算一遍,没有什么性能提升,仅仅是为了得出正确的遮挡结果,会造成大量的无用计算,因为每个像素点上肯定重叠了很多计算。
因此 现代GPU中运用了Early-Z的技术,在Vertex阶段和Fragment阶段之间(光栅化之后,fragment之前)进行一次深度测试,如果深度测试失败,就不必进行fragment阶段的计算了,因此在性能上会有很大的提升。但是最终的ZTest仍然需要进行,以保证最终的遮挡关系结果正确。前面的一次主要是Z-Cull为了裁剪以达到优化的目的,后一次主要是Z-Check,为了检查,如下图:
放到我的自制小管线里在红色箭头这个地方
5 Queue 渲染队列
虽然z-buffer算法原理看上去很简单,但是涉及到渲染场景中不同属性的物体时,还需要做出更多的考量与设置。如何渲染场景中多个物体,给他们如何排序这个问题非常重要。在unity中默认有几种渲染队列,也可以自行定义,比如2001就是在Geometry后,AlphaTest之前。
Unity中设置渲染队列:Tags { “Queue” = “Transparent”},默认是Geometry
- 不透明物体的渲染顺序:从前往后
- 透明物体的渲染顺序:从后往前(OverDraw)
对于渲染半透明物体的情况,就更值得注意啦,如下图,半透明物体的像素往往会与其背后物体的像素进行混合,所以从后往前,很好理解!
参考
更多推荐
所有评论(0)