没啥用的主页
学习杂记
Shader模板测试
Nov 23 2022

在渲染管线中,我们会经过一系列流程的处理,才能在屏幕上显示出来一个像素的颜色,![[Pasted image 20221120151247.jpg]],也就是图中所示,而在逐片元操作中,我们便会进行各种的测试与判断,如

  1. 像素使用权(比如窗口化下我们只有某一部分的像素的使用权力)
  2. 裁剪测试
  3. 透明度测试 当低于某个阈值的时候便舍弃这一个像素。
  4. 模板测试(StencilTest)
  5. 深度测试 :会有一张深度表,记载了每一个片元的深度值,如果我们的像素深度小于了表中的值,那么便会覆写,否则便会放弃该片元。
  6. 透明度混合 :我们会将颜色缓冲区的值拿出来与片元颜色混合,再写入进去,由于透明物体会看到其背后的物体,所以透明物体要关闭深度写入。
    经过以上的测试。最终输出该片元的颜色到我们的帧缓冲区。

模板测试与深度缓冲,颜色混合一样,都有一张表来存储每一个片元的值,在unity中默认为0,最大为255。虽然深度测试是不可编程的,但他是高度可配置的,我们可以通过设置参数来达到一些自己想要的目的。一个片元在模板测试的时候,他会判断自己的模板值和表中的值,然后根据设置的值来达到自己想要的目的。并选择性修改表中的模板值。在unity中他的语法如下
其中的读写掩码是在比较之前进行的。他们会将模板值和掩码值想与,l当在比较判断的时候也会将缓冲区的值与读掩码想与了以后再来判断。如
referenceValue & readMask

stencil{
    Ref referenceValue //当前片元的模板值
    ReadMask readMask // 读掩码
    WriteMask writeMask //写掩码
    Comp ComparisonFunction // 比较函数
    Pass stencilOperation // 测试通过后所进行的操作
    Fail stencilOperation // 测试未通过所进行的操作
    ZFail stencilOperation // 模板测试未通过 深度测试通过了的操作
}

其中我们的比较函数有以下几种可选,其中左为片元的模板值,右为缓冲区的模板值
![[v2-43702e4cfe572f6ba73860127c4cd4ca_720w.webp]]
更新函数有以下几种![[v2-a53fad0de7436efadaa1fb6e6d1a52f2_720w.webp]]

例子

用的最多的效果是一种遮罩的效果。我们需要一个蒙版。然后有他来更新缓冲区的值,但是不会写入颜色缓冲区。这样便可以让通过了模板测试的后续才写入颜色缓冲区。蒙版代码如下

    Shader "Unlit/StencilMask"
    {
    Properties
    {
        _MaskId ("id", int) = 1
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" "Queue" = "Geometry+1" }
        ColorMask 0
        ZWrite Off
        Stencil{
            Ref[_MaskId]
            Comp Always
            pass replace
        }
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
        struct appdata {
            float4 vertex : POSITION;
        };
        struct v2f {
            float4 pos : SV_POSITION;
        };
          v2f vert(appdata v) {
            v2f o;
            o.pos = UnityObjectToClipPos(v.vertex);
            return o;
        }
        half4 frag(v2f i) : SV_Target{
            return half4(1,1,1,1);
        }
            ENDCG
        }
    }
    }

他的渲染队列在不透明物体后面,然后更改缓冲区的值。以达到一种蒙版的效果。物体的代码如下

    Shader "Unlit/Lit StencilMask"
    {
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _Id("Id",int) = 1
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" "Queue" ="Geometry+2"  }
        LOD 100
        Stencil{
            Ref[_Id]
            Comp equal
        }
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            // make fog work
            #pragma multi_compile_fog
            #include "UnityCG.cginc"
            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };
            struct v2f
            {
                float2 uv : TEXCOORD0;
                UNITY_FOG_COORDS(1)
                float4 vertex : SV_POSITION;
            };
            sampler2D _MainTex;
            float4 _MainTex_ST;
            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                UNITY_TRANSFER_FOG(o,o.vertex);
                return o;
            }
            fixed4 frag (v2f i) : SV_Target
            {
                // sample the texture
                fixed4 col = tex2D(_MainTex, i.uv);
                // apply fog
                UNITY_APPLY_FOG(i.fogCoord, col);
                return col;
            }
            ENDCG
        }
    }
    }


最终效果图就如图所示