删除多个元素(C语言实现)

使用nodejs爬取某网站数据

  返回  

图形学进阶——Early-Z和Z-prepass

2021/7/21 0:03:39 浏览:

关于Early-Z和Z-prepass

百人计划学习链接:【技术美术百人计划】图形 3.5 Early-z和Z-prepass

一、回顾深度测试

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

渲染管线中深度测试

2. 深度测试的作用与实现

作用:为了解决物体可见遮挡性的问题
在这里插入图片描述
进行深度测试后,能够确保只有在最前面的片元才会最终渲染出来
在这里插入图片描述

深度测试的流程:
在这里插入图片描述

3. 深度测试的问题

由于在传统的渲染管线中,深度测试的位置是在Blend之前,而此时的片元已经进行了片元着色器的计算,如果深度测试后被舍弃,就会造成大量的无用计算
在这里插入图片描述
因此,为了解决这个问题,需要使用Early-Z技术

二、提前深度测试——Early-Z

1. 什么是Early-Z?

就是在光栅化之后,片元着色器之前进行一次深度测试,没有通过测试的片元则不进行之后的片元着色器计算,因此来提高性能。
在这里插入图片描述

渲染管线中的Early-Z:
在这里插入图片描述
注:Ealry-Z中同样可以使用模板测试

2 . Early-Z的问题

在以下几种情况中,Ealry-Z会不生效

  • 开启Alpha Test 或 clip/discard 等手动丢弃片元操作
  • 手动修改GPU插值得到的深度
  • 开启Alpha Blend
  • 关闭深度测试Depth Test

注:前两点情况原理类似,由于手动进行了片元的丢弃,会导致深度测试筛选出的片元也可能会被舍弃;第三点是由于开启了Alpha Blend一般会关闭深度写入,所以也不会生效;第四点关闭深度测试自然不会生效

如何高效利用Ealry-Z

当物体按照离屏幕由远及近的顺序渲染时,每一个物体的像素依旧会通过深度检测,因此并不会减少像素着色过程。
在这里插入图片描述

三、使用Z-Prepass

1. 什么是Z-Prepass

Z-Prepass首先对需要被渲染的物体先执行一遍渲染管线,但这个管线的像素着色器不执行任何操作,通过这种方法,借助深度检测机制,将离屏幕最近的物体的深度值写入深度缓冲区。
然后执行第二遍渲染管线,这次的管线使用第一遍渲染产生的深度缓冲区,并利用early-z技术,使得只有与深度缓冲区中深度值相等的被渲染。
在这里插入图片描述
Shader中的使用案例:
在这里插入图片描述
但是,这样会有动态批处理的问题
在这里插入图片描述
一个物体拥有多个Pass,是无法进行动态批处理的,因为也会带来Draw Call的问题
在这里插入图片描述

2. 如何解决

使用提前分离的方式:
在这里插入图片描述
具体的案例可以参考雨松MOMO的这篇文章:使用URP下的RendererFeature
在这里插入图片描述

四、关于Z-prepass的其他内容

Z-prepass也可以用来解决透明渲染的问题
在这里插入图片描述
Z-prepass的计算消耗

Z-prepass真的有用吗?
在这里插入图片描述
论坛中的计算中可以看出使用了Z-prepass反而增大了消耗

因此,需要根据不同的场景来考虑是否要使用Z-prepass(例如有大量OverDraw的场景中使用Z-prepass可以很好的减小消耗)

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

在这里插入图片描述
首先的方案是使用了3个Pass。

  • 第一个pass渲染不透明的部分,并写入深度
  • 剔除正面,渲染背面,并关闭深度写入
  • 剔除背面,渲染正面,写入深度
    在这里插入图片描述
    这样的做法会带来特别多的OverDraw,因此需要使用Ealry-Z和Z-prepass进行改善
    在这里插入图片描述
    但是,early-z是不能使用透明度测试的,所以需要使用一个非常简单的shader来进行透明度测试,并生成一个Z-Buffer
    最终需要用到4个Pass
  • 第一个pass用于生成Z Buffer:开启透明度测试仅通过不透明的测试,并关闭背面剔除,开启深度写入,关闭颜色缓冲区写入,只返回透明度值
  • 之后的3个PASS与之前类似:第一个是渲染不透明物体,并剔除背面,设置深度测试为等于,关闭深度写入
  • 第二个渲染背面,剔除正面,并关闭深度写入,深度测试为小于
  • 第三个渲染正面,剔除背面,并开启深度写入,深度测试同上

在这里插入图片描述

作业

1. 做下Z-prepass的效果测试

测试一下使用Z-prepass的透明物体效果与消耗

先使用一个正常的透明shader

Shader "Unlit/TestAlphaShader"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _Alpha("Alpha", Range(0,1)) = 1
    }
    SubShader
    {
        Tags { "Queue"="Transparent" }
        Pass
        {
            blend SrcAlpha OneMinusSrcAlpha
            ZWrite Off
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
            float _Alpha;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 col = tex2D(_MainTex, i.uv);
                col.a = _Alpha;
                return col;
            }
            ENDCG
        }
    }
}

效果如下
在这里插入图片描述
由于没有进行升读写入,会得到一个奇怪的透明效果

使用两个Pass来渲染,一个开启深度写入但不输出颜色,另一个关闭深度写入

Pass
{
    ZWrite On
    ColorMask 0
}

在这里插入图片描述
效果正常,但是在framebuffer中可以看出多了一次drawcall
在这里插入图片描述

2. 总结下Early-Z的限制

  • 不能使用Alpha Test或者手动修改深度插值
  • 物体如果是从后往前渲染的(例如透明物体),则起不到任何优化作用
  • 不要无脑使用Z-preprass,否则会导致负优化

联系我们

如果您对我们的服务有兴趣,请及时和我们联系!

服务热线:18288888888
座机:18288888888
传真:
邮箱:888888@qq.com
地址:郑州市文化路红专路93号