Depth Test 深度测试

Depth Test 深度测试_第1张图片
深度测试在模板测试之后,透明度混合之前,如果开启了深度测试,GPU会把该片元的深度值和已经存在于深度缓冲区(Depth Buffer 或者叫 Z-Buffer)中的深度值进行比较,这个比较函数可由开发者设置的,例如片元的深度值大于缓冲区深度值时舍弃该片元

从逻辑上理解

if(ZWrite On && (currentDepthValue ComparisonFunction DepthBufferValue))
	写入深度
else
	丢弃像素
if(currentDepthValue ComparisonFunction DepthBufferValue)
	写入颜色缓冲
else
	不写入颜色缓冲

深度缓冲中,对于每一个像素都存储一个深度值,注意这个深度值是非线性的,近处的精度高,远处的精度低。ZTest分为通过和不通过两种情况,ZWrite分为开启和关闭两种情况,一共四种情况

  1. 深度测试通过,深度写入开启,写入深度缓冲区,写入颜色缓冲区
  2. 深度测试通过,深度写入关闭,不写深度缓冲区,写入颜色缓冲区
  3. 深度测试失败,深度写入开启,不写深度缓冲区,不写颜色缓冲区
  4. 深度测试失败,深度写入关闭,不写深度缓冲区,不写颜色缓冲区

深度测试的比较方法 ComparisonFunction ,和模板测试差不多

比较方法 描述
Greater 深度大于当前缓冲则通过
GEqual 深度大于等于当前缓存则通过
Less 深度小于当前缓存则通过
LEqual 深度小于等于当前缓存则通过
Equal 深度等于当前缓存则通过
NotEqual 深度不等于当前缓存则通过
Always 深度不论如何都通过
Never 深度不论如何都不通过

默认是ZWrite On和ZTest LEqual

  • 不透明物体的渲染顺序:从前往后
  • 透明物体的渲染顺序:从后往前

下面几种情况显示,ZTest,ZWrite和渲染队列如何影响渲染,蓝色立方体离摄像机最近,红色离的最远
Depth Test 深度测试_第2张图片
这是默认设置,从近到远,先渲染蓝色,深度缓冲区默认是无穷大,蓝色深度小于无穷大,深度测试通过,写入深度,写入颜色缓冲,再渲染绿色,在蓝色和绿色相交的部分,绿色的深度大于蓝色,深度测试不通过,其他部分正常写入深度,最后渲染红色同理
Depth Test 深度测试_第3张图片
渲染蓝色,深度测试通过,但是它关闭的深度写入,不写入深度值,写入颜色缓冲,渲染绿色,深度测试通过,写入深度值和颜色,替换掉之前的蓝色
Depth Test 深度测试_第4张图片
渲染蓝色,和第一张图情况一样,绿色的ZTest设置为Always,不管深度值如何都通过深度测试,写入深度和颜色,重叠部分覆盖蓝色深度值
Depth Test 深度测试_第5张图片
红色ZTest也改成Always,原理和绿色覆盖蓝色是一样的
Depth Test 深度测试_第6张图片
绿色的渲染队列调整为Geometry+1,越大越后渲染,渲染顺序是:蓝,红,绿,先按照渲染队列排序,同一个队列,不透明物体按照从近到远排列
Depth Test 深度测试_第7张图片
绿色ZTest选择Greater,表示当前深度大于深度缓冲区深度才能通过,蓝绿重叠部分,绿色深度大于蓝色深度,深度测试通过,其他部分绿色深度小于无穷大,深度测试不通过

可以使用下面的Shader验证

Shader "MyCustom/ZTestZWrite"
{
    Properties
    {
        _Color ("_Color", Color) = (1, 0, 0, 1)
        //自定义枚举
        [Enum(Off, 0, On, 1)]_ZWriteMode ("ZWriteMode", Float) = 1
        [Enum(UnityEngine.Rendering.CompareFunction)]_ZTestComp ("ZTestComp", Int) = 4
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" "Queue"="Geometry" }
        ZWrite [_ZWriteMode]
        ZTest [_ZTestComp]

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
            };

            struct v2f
            {
                float4 vertex : SV_POSITION;
            };

            float4 _Color;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                return _Color;
            }
            ENDCG
        }
    }
}

XRay效果

Depth Test 深度测试_第8张图片
使用深度测试,实现角色在墙后显示描边的效果,这里用2个Pass实现
第一个Pass渲染描边,角色的深度大于墙的深度,为了让角色通过深度测试需要设置 ZTest Greater,并且需要保持正常的遮挡关系,所以渲染描边时不能写入深度,实现这种半透明效果还要设置混合因子
第二个Pass就是正常的渲染

Shader "MyCustom/XRay"
{
    Properties
    {
        _MainTex("Base 2D", 2D) = "white" {}
        _XRayColor ("XRay Color", Color) = (1, 0, 0, 1)
    }
    SubShader
    {
        CGINCLUDE
        #include "UnityCG.cginc"

        float4 _XRayColor;
        
        struct v2f
        {
            float4 pos : SV_POSITION;
            float3 normal : NORMAL;
            float3 viewDir : TEXCOORD0;
            fixed4 color : COLOR;
        };

        v2f vertXRay(appdata_base v)
        {
            v2f o;
            o.pos = UnityObjectToClipPos(v.vertex);
            //模型顶点到相机方向(模型空间)
            o.viewDir = ObjSpaceViewDir(v.vertex);
            o.normal = v.normal;

            float3 normal = normalize(v.normal);
            float3 viewDir = normalize(o.viewDir);
            float rim = 1 - dot(normal, viewDir);
            o.color = _XRayColor * rim;
            return o;
        }

        fixed4 fragXRay(v2f i) : SV_Target
        {
            return i.color;
        }

        sampler2D _MainTex;
        float4 _MainTex_ST;

        struct v2f2
        {
            float4 pos : SV_POSITION;
            float2 uv : TEXCOORD0;
        };

        v2f2 vertNormal(appdata_base v)
        {
            v2f2 o;
            o.pos = UnityObjectToClipPos(v.vertex);
            o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
            return o;
        }

        fixed4 fragNormal(v2f2 i) : SV_Target
        {
            return tex2D(_MainTex, i.uv);
        }
        ENDCG

        //XRay
        Pass
        {
            Tags { "RenderType"="Transparent" "Queue"="Transparent" }
            ZWrite Off
            ZTest Greater
            Blend SrcAlpha One
            
            CGPROGRAM
            #pragma vertex vertXRay
            #pragma fragment fragXRay
            ENDCG
        }
        
        //正常绘制
        Pass
        {
            Tags { "RenderType"="Opaque" "Queue"="Geometry" }
            ZWrite On
            ZTest LEqual
            
            CGPROGRAM
            #pragma vertex vertNormal
            #pragma fragment fragNormal
            ENDCG
        }
    }
}

Depth Test 深度测试_第9张图片
对于多Pass渲染,Unity会从多个Pass里选择渲染队列最靠前的,把物体放在这个队列渲染,这里第一个Pass是Transparent,第二个Pass是Geometry,Geometry靠前渲染就放在了Geometry队列,然后会根据Pass顺序执行

参考

【技术美术百人计划】图形 3.1 深度与模板测试 传送门效果示例

你可能感兴趣的:(技术美术,unity)