Unity Shader之模板測試 原理與應用

一、認識模板測試

模板測試是在逐片元測試幾個階段中的第一個階段,它是在深度測試和融混之前的;在平時,我們可能會接觸深度測試比較多一點,所以接下來在認識模板測試的時候,經常會與深度測試做比較,以輔助大家來理解模板測試;

首先,想要進行模板測試,就需要有一個緩衝區,這個緩衝區就是模板緩衝;只有在建立一個窗口過程中預先請求模板緩衝,才能夠進行模板測試,如果沒有模板緩衝,則默認通過模板測試;很多窗口庫以及Unity都會幫我們做好這件事;

回顧深度緩衝相關

我們先回顧一下深度緩衝的邏輯,我們做深度測試,那麼就需要比較深度值,用於比較的兩個值分別是什麼呢,一個是通過幾何階段計算得到的當前像素的z值,另一個就是當前深度緩衝區內的z值,兩者做一個比較;而深度緩衝的默認值,也就是清除深度緩衝後,它的所有值都爲1,因爲我們知道深度緩衝的值的範圍是[0,1],越小的值即爲越靠近相機的;

理解模板緩衝

模板緩衝和深度緩衝的原理類似,它也是需要做模板測試,那用於比較的兩個值是什麼呢,一個是模板緩衝中的值,一個是設置的參考值;而模板緩衝的默認值,就是255(因爲模板緩衝的數據是8位的);和深度緩衝不同的是,如果要進行深度寫入,寫入值是每個像素計算出的深度值,而在模板緩衝中如果要進行寫入,寫入值就是當前的參考值;

一些關於模板緩衝的其它概念

掩碼mask,它允許我們設置一個掩碼mask,當模板緩衝中的值與模板值比較前,其實模板緩衝的值是需要與掩碼mask進行and操作的;當然這裏可以不用關心,一般情況下都是所有位置1或置0;

模板比較函數 ,用於測試的函數邏輯;

Pass/ZFail/Fail,一般在做完測試後都會有這三種情況,通過模板測試、通過模板測試但未通過深度測試、未通過模板測試;

操作函數,上述三種情況,都需要有一個處理模板緩衝值的方式,這一點和深度緩衝一樣;

與深度緩衝相關的概念都在這裏,下面進入到Unity Stencil Test的學習;

二、Unity ShaderLab Stencil的語法

這一部分主要參考了Unity的官方文檔:Unity Manual ShaderLab-Stencil

這裏先介紹語法部分,這些語法和模板緩衝原理中的概念是幾乎一致的;

Ref,Ref refVal,指定一個用於比較的參考值;

ReadMask maskVal,指定讀取掩碼,這個就相當於上面說的遮罩掩碼,在讀取模板緩衝的值來進行比較的時候,會與掩碼進行and運算,默認值爲255;

WriteMask maskVal,指定寫入掩碼,這個上面沒有提到,是unity獨自提供的,和ReadMask類似,在寫入模板緩衝值的時候,會與掩碼進行與操作,然後將結果寫入模板緩衝,默認值爲255;

Comp comparisonFunction,比較函數,unity提供了8中枚舉,Greater,GEqual,Less,LEqual,Equal,NotEqual,Always,Never;通過字面意思理解即可;

Pass/ZFail//Fail stencilOp,爲每一種結果指定一個操作,一共有八種枚舉,Keep,Zero,Replace,IncrSat,IncrWrap,DecrSat,DecrWrap,Invert;

三、實際應用

這裏直接借用官方案例做一個簡單的分析;

第一個shader,指定渲染隊列爲Geometry2000,然後設置Pass爲replace,這樣就把該shader渲染的物體區域的深度緩衝值都設爲了2;將該shader賦值給一個sphere;

Shader "Stencil/Red" {
    SubShader {
        Tags { "RenderType"="Opaque" "Queue"="Geometry"}
        Pass {
            Stencil {
                Ref 2
                Comp always
                Pass replace
            }
        
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            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,0,0,1);
            }
            ENDCG
        }
    } 
}

第二個shader,渲染隊列爲2001, 意味着在紅球之後渲染,然後設置Ref爲2,比較函數爲equal,這樣的話,如果這個用於渲染綠球,綠球就顯示和紅球重合的一部分了;

Shader "Green" {
    SubShader {
        Tags { "RenderType"="Opaque" "Queue"="Geometry+1"}
        Pass {
            Stencil {
                Ref 2
                Comp equal
                Pass keep 
                ZFail decrWrap
            }
        
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            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(0,1,0,1);
            }
            ENDCG
        }
    } 
}

 如果我們不走模板測試,應該是左圖,現在做了模板測試,就只有模板緩衝爲2的區域可以被渲染,所以出現右圖效果;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章