Version: 2020.2
ShaderLab: Pass Tags
ShaderLab: Name

ShaderLab: Stencil

You can use the stencil buffer as a general purpose per pixel mask for saving or discarding pixels.

El buffer de la plantilla es normalmente un entero de 8 bits por píxel. El valor se puede escribir para incrementar o decrementar. Las llamadas de dibujo posteriores pueden probar contra el valor, para decidir si un píxel debe descartarse antes de ejecutar el pixel shader.

Sintaxis

Ref

    Ref referenceValue

El valor con el que se comparará (si Comp es algo más que always) y/o el valor que se escribirá en el buffer (si se cambia Pass, Fail o ZFail). 0–255 entero.

ReadMask

    ReadMask readMask

Una máscara de 8 bits como un entero 0–255, que se utiliza al comparar el valor de referencia con el contenido del buffer (referenceValue & readMask) comparisonFunction (stencilBufferValue & readMask). Predeterminado: 255.

WriteMask

    WriteMask writeMask

An 8 bit mask as an 0–255 integer, used when writing to the buffer. Note that, like other write masks, it specifies which bits of stencil buffer will be affected by write (i.e. WriteMask 0 means that no bits are affected and not that 0 will be written). Default: 255.

Comp

    Comp comparisonFunction

Función utilizada para comparar el valor de referencia con el contenido actual del buffer. Predeterminado: always.

Pass

    Pass stencilOperation

Qué hacer con el contenido de la memoria intermedia si la prueba de stencil (plantilla) (y la prueba de profundidad) pasa. Predeterminado: keep.

Fail

    Fail stencilOperation

Qué hacer con el contenido del buffer si la prueba del stencil (plantilla) falla. Predeterminado: keep.

ZFail

    ZFail stencilOperation

Qué hacer con el contenido de la memoria intermedia si la prueba del stencil (plantilla) pasa, pero la prueba de profundidad falla. Predeterminado: keep.

Comp, Pass, Fail y ZFail se aplicarán a la geometría frontal, a menos que se especifique Cull Front, en cuyo caso es la geometría orientada hacia atrás. También puede especificar explícitamente el estado del stencil de dos caras definiendo CompFront, PassFront, FailFront, ZFailFront (para geometría frontal) y CompBack, PassBack, FailBack, ZFailBack (para la geometría orientada hacia atrás).

Función de comparación

La función de comparación es una de las siguientes:

Greater Sólo renderiza píxeles cuyo valor de referencia sea mayor al valor en el buffer.
GEqual Sólo renderiza píxeles cuyo valor de referencia sea mayor o igual al valor en el buffer.
Less Sólo renderiza píxeles cuyo valor de referencia sea menor al valor en el buffer.
LEqual Sólo renderiza píxeles cuyo valor de referencia sea menor o igual al valor en el buffer.
Equal Sólo renderiza píxeles cuyo valor de referencia sea igual al valor en el buffer.
NotEqual Sólo renderiza píxeles cuyo valor de referencia difiera al valor en el buffer.
Always Haga que la prueba del stencil (plantilla) siempre pase.
Never Hace que la prueba de stencil (plantilla) siempre falle.

Operación Stencil

Operación Stencil es una de las siguientes:

Keep Mantiene el contenido actual del buffer.
Zero Escribo 0 al buffer.
Replace Escribe el valor de referencia al buffer.
IncrSat Incrementa el valor actual en el buffer. Si el valor es 255 ya, permanece en 255.
DecrSat Reduce el valor actual en el buffer. Si el valor es 0 ya, se mantiene en 0.
Invert Niega todos los bits.
IncrWrap Incrementa el valor actual en el buffer. Si el valor es 255, se convierte en 0.
DecrWrap Reduce el valor actual en el buffer. Si el valor es 0, se convierte en 255.

Deferred rendering path

Stencil functionality for objects rendered in the deferred rendering path is somewhat limited, as during the G-buffer pass and lighting pass, Unity uses the stencil buffer for other purposes. During those two stages, the stencil state defined in the shader will be ignored. Because of that, you cannot mask out these objects based on a stencil test, but they can still modify the buffer contents, to be used by objects rendered later in the frame. Objects rendered in the forward rendering path following the deferred path (e.g. transparent objects or objects without a surface shader) will set their stencil state normally again.

These bits are used for the stencil buffer in the deferred rendering path:

  • Bit #7 (value=128) indicates any non-background object.
  • Bit #6 (value=64) indicates non-lightmapped objects.
  • Bit #5 (value=32) is not used by Unity.
  • Bit #4 (value=16) is used for light shape culling during the lighting pass, so that the lighting shader is only executed on pixels that the light touches, and not on pixels where the surface geometry is actually behind the light volume.
  • Lowest four bits (values 1,2,4,8) are used for light layer culling masks.

It is possible to operate within the range of the unused bits using the stencil read and write masks, or you can force the camera to clean the stencil buffer after the lighting pass using Camera.clearStencilAfterLightingPass.

Ejemplo

The first example shader will write the value ‘2’ wherever the depth test passes. The stencil test is set to always pass.

Shader "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
        }
    } 
}

El segundo shader pasará sólo para los píxeles que pasó el primer shader (rojo), porque está comprobando la igualdad con el valor ‘2’. También disminuirá el valor en el buffer dondequiera que falle la prueba de Z.

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
        }
    } 
}

El tercer shader sólo pasará cuando el valor del stencil sea “1”, lo que significa sólo los píxeles en la intersección de las dos esferas rojas y verdes - es decir, donde el stencil se configure en “2” por el shader rojo y se decrementa a “1” Por el shader verde.

Shader "Blue" {
    SubShader {
        Tags { "RenderType"="Opaque" "Queue"="Geometry+2"}
        Pass {
            Stencil {
                Ref 1
                Comp equal
            }
        
            CGPROGRAM
            #include "UnityCG.cginc"
            #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,0,1,1);
            }
            ENDCG
        }
    }
}

El resultado:

Otro ejemplo de un efecto más dirigido. La esfera se procesa primero con este shader para marcar las regiones apropiadas en el buffer del stencil:

Shader "HolePrepare" {
    SubShader {
        Tags { "RenderType"="Opaque" "Queue"="Geometry+1"}
        ColorMask 0
        ZWrite off
        Stencil {
            Ref 1
            Comp always
            Pass replace
        }

        CGINCLUDE
            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,0,1);
            }
        ENDCG

        Pass {
            Cull Front
            ZTest Less
        
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            ENDCG
        }
        Pass {
            Cull Back
            ZTest Greater
        
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            ENDCG
        }
    } 
}

Y luego se vuelve a presentar como un standard surface shader, con la excepción de la eliminación de la cara frontal, la prueba de profundidad inhabilitada y la prueba del stencil que descartan los píxeles previamente marcados:

Shader "Hole" {
    Properties {
        _Color ("Main Color", Color) = (1,1,1,0)
    }
    SubShader {
        Tags { "RenderType"="Opaque" "Queue"="Geometry+2"}

        ColorMask RGB
        Cull Front
        ZTest Always
        Stencil {
            Ref 1
            Comp notequal 
        }

        CGPROGRAM
        #pragma surface surf Lambert
        float4 _Color;
        struct Input {
            float4 color : COLOR;
        };
        void surf (Input IN, inout SurfaceOutput o) {
            o.Albedo = _Color.rgb;
            o.Normal = half3(0,0,-1);
            o.Alpha = 1;
        }
        ENDCG
    } 
}

El resultado:

ShaderLab: Pass Tags
ShaderLab: Name