ShaderLab: Stencil

25 篇文章 0 订阅
25 篇文章 0 订阅

ShaderLab: Stencil

可以使用stencil buffer作为通用像素掩码来保存或丢弃像素。

Stencil buffer通常为8 bit整数每像素。该值可以被写为递增或递减。后续的调用,可以测试该值,来决定运行pixel shader前,是否应该将像素丢弃

Syntax

Ref

  Ref referenceValue

该值用来比较(当 Comp 设置为非always)并/或 用来写入到buffer(如果任何Pass,Fail,ZFail被设置来代替他)。为 0–255 整数.

WriteMask

   WriteMask writeMask

一个8 bit的mask,值为0-255的整数,当写入buffer时使用。注意,和其他写掩码一样,它指定的模版缓冲(stencil buffer)会被写操作影响。(例如:WriteMask 0 意味着没有bit会受影响,而不是0值会被写入)。 默认值为: 255.

Comp

    Comp comparisonFunction

该函数用来比较reference值和当前buffer中的内容。默认为: always.

Pass

    Pass stencilOperation

表示要做什么,当buffer内容通过了测试(以及深度测试)。 默认: keep.

Fail

    Fail stencilOperation

表示要做什么,当buffer的内容没有通过测试。 默认: keep.

ZFail

    ZFail stencilOperation

做什么当buffer内容通过了stencil测试,但是没通过深度测试。 默认: keep.

Comp, Pass, Fail 和 ZFail 将被提供给前前置几何图像,除非 Cull Front被指定,在这种情况下是提供给后置几何图像。你也可以明确的指出前后两面的stencil通过定义 CompFront, PassFront, FailFront, ZFailFront (for front-facing geometry), 和 CompBack, PassBack, FailBack, ZFailBack (for back-facing geometry).

Comparison Function

比较函数表格:

functiondescription
Greater只渲染reference值比buffer中值大的像素。
GEqual只渲染reference值比buffer中值大或等于的像素。
Less只渲染reference值比buffer中值小的像素。
LEqual只渲染reference值比buffer中值小或等于的像素。
Equal只渲染reference值比buffer中值等于的像素。
NotEqual只渲染reference值比buffer中值不等于的像素。
Always让stencil测试总是通过。
Never让stencil测试总是失败

Stencil Operation

Stencil 操作表格:

operationdescription
keep保留当前buffer内容。
Zero写入0到当前buffer。
Replace写入reference值到当前buffer。
IncrSat增加当前buffer的值。如果已经达到255则保持255。
DecrSat减少当前buffer的值。如果已经达到0则保持0。
Invert反转所有位。
IncrWrap增加当前buffer的值。如果已经达到255则变为0。
DecrWrap减少当前buffer的值。如果已经达到0则变为255。

Deferred rendering path(延迟渲染路径)

Stencil 功能用在对象在延迟渲染路径的渲染有一定的限制,比如在 base pass或 lighting pass过程中用于其他用途。在这两个阶段stencil状态被定义在shader中会被忽略并且只有在最终的pass中才会被考虑到。因为这样不可能在stencil测试掩码输出这些对象,但是stencil还是可以改变buffer内容,会在每帧对象渲染的最后被调用。对象在deferred path之的的forward rendering path(例如:transparent objects or objects without a surface shader)中将会把他们的stencil设置会正常状态。

延迟渲染路径,使用三个高位的stencil buffer bits,再加上四个最高位 - 这取决于有多少光照遮罩层在该场景被使用。 它可能实现“clean” bits操作通过stencil的read和wirte mask,或者你可以强制相机清除stencil buffer 在光照pass后使用Camera.clearStencilAfterLightingPass.

Example

第一个例子,将写入值 ‘2’无论深度测试在哪通过。模板测试总是通过。

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

第二个shader将通过只当上一个shader(red)通过时,因为它检查了值是否等于‘2’。如果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
        }
    } 
}

第三方shader将在模板值为‘1’时通过,所以只有, 所以只有红色和绿色球体相交处的像素 - 因为, 该模版被red shader设置为 ‘2’ 并且被green shader减到‘1’。

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

The result:

result

另一个更直接效果的例子。 首先用这个着色器渲染球体,以标记模板缓冲区中的适当区域:

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

然后再作为一个完全的标准表面着色器渲染一次, 除了正面的剔除, 停用深度测试和模版测试,丢弃之前标记的像素:

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

The result:

result2


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值