一、深度测试DepthTest

1.传统渲染管线中的深度测试

3.5 Early-Z 与 Z Prepass_顶点着色器

3.5 Early-Z 与 Z Prepass_顶点着色器_02

深度测试在逐片元操作中的第二步

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

3.5 Early-Z 与 Z Prepass_游戏_03

3.5 Early-Z 与 Z Prepass_Test_04

3.5 Early-Z 与 Z Prepass_游戏_05

3.深度测试的逻辑

3.5 Early-Z 与 Z Prepass_深度测试_06

for(each triangle T) //对每一个三角形	
{				
    for(each fragment(x,y,z) in T)//对每一个三角形中的片元 
    {	   	
        if(fragment.z < ZBuffer[x,y])//深度测试:如果片元深度小于ZBuffer深度
        {	
            FrameBuffer[x,y] = fragment.rgb; //更新颜色     
            ZBuffer[x,y] = fragment.z;		 //更新深度     
              
        }        
        else
        {					
            //深度测试失败        		
            //什么都不做,片元数据被丢弃       
        }    
    }
}

4.深度测试带来的问题

3.5 Early-Z 与 Z Prepass_游戏_07

物体2与物体3的计算得到的片元都将会被丢弃

先将1、2、3进行片元着色后,在进行深度测试后抛弃2、3,则2、3在片元部分做的计算都浪费掉了。

二、提前深度测试Early-Z

应用阶段(CPU)->几何阶段(顶点着色器)->early-z(提前深度测试)->光栅化阶段(片元着色器)->各种测试(深度测试,透明度测试,模板测试等)->颜色缓冲区(buffer)

Early-Z失效:

1. 开启Alpha Test 或 clip/discard 等手动丢弃片元操作

2. 手动修改GPU插值得到的深度

3. 开启Alpha Blend

4. 关闭深度测试Depth Test

3.5 Early-Z 与 Z Prepass_深度测试_08

如果按照3-2-1顺序渲染测试,Early-Z将不会带来任何优化效果

三、使用Z-Prepass

方式一:双Pass法

  • 使用两个Pass:
  • 在第一个Pass即Z-Prepass中仅仅值写入深度,不计算输出任何颜色。
  • 在第二个Pass中关闭深度写入,并且将深度比较函数设置为相等。

3.5 Early-Z 与 Z Prepass_顶点着色器_09

动态批处理问题:

3.5 Early-Z 与 Z Prepass_顶点着色器_10

DrawCall问题:

3.5 Early-Z 与 Z Prepass_Test_11

方式二:提前分离的PrePass

  • 仍然使用两个Pass,但:
  • 将原先第一个Pass(即Z-Prepass)单独分离出来未单独一个Shader,并先使用这个Shader将整个场景的Opaque的物体渲染一遍。
  • 而原先材质只剩下元原先的第二个Pass,仍然关闭深度写入,并且将深度比较函数设置为相等。

例子:使用URP下的RendererFeature

3.5 Early-Z 与 Z Prepass_Test_12

3.5 Early-Z 与 Z Prepass_深度测试_13

Z-Prepass也是透明渲染的一种解决方案

3.5 Early-Z 与 Z Prepass_顶点着色器_14

3.5 Early-Z 与 Z Prepass_Test_15

单Pass不写入深度的透明渲染 第一Z-Prepass写入深度,第二Pass(Ztest Equal)透明渲染

四、Z-Prepass所带来的问题

Z-Prepass的计算消耗

3.5 Early-Z 与 Z Prepass_游戏_16

3.5 Early-Z 与 Z Prepass_顶点着色器_17

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

3.5 Early-Z 与 Z Prepass_顶点着色器_18

3.5 Early-Z 与 Z Prepass_Test_19

3.5 Early-Z 与 Z Prepass_深度测试_20

3.5 Early-Z 与 Z Prepass_深度测试_21