ShadersRoom - Stencil Buffer

老规矩,先上一张效果图:

Stencil.gif

这个功能效果的核心点是模板缓存Stencil buffer,和深度缓存类似,模板缓存可以为屏幕上的每个像素点保存一个无符号整数值(通常的话是个8位整数,范围0-255),通过比较这个数值可以控制是否需要更新当前像素的颜色缓存,这个过程叫做模板测试,默认情况下,总是测试通过的,可以通过以下参数来控制这个过程:

  • Ref 自定义数值,用来和模板缓冲中的值进行比较,范围0-255,默认值是0
  • ReadMask 对当前参考值和已有值进行mask操作,默认值255;
  • WriteMask 写入Mask操作,默认值255;
  • Comp 比较方法,Ref定义的值和当前像素缓存上的值进行比较,有以下参数,默认值always:
      Greater - 大于
      GEqual - 大于等于
      Less - 小于
      LEqual - 小于等于
      Equal - 等于
      NotEqual - 不等于
      Always - 永远通过
      Never - 永远通不过
  • Pass 模版测试和深度测试都通过时,进行的操作
  • Fail 模版测试和深度测试都失败时,进行的操作
  • ZFail 模版测试通过而深度测试失败时,进行的操作
      Pass,Fail,ZFail 默认值都是Keep,可使用的参数如下:
      Keep 保持(即不做处理)
      Zero 归零
      Replace 替换(参考值替换原有值)
      IncrSat 增加1,最大到255
      DecrSat 减少1,最小到0
      Invert 反转所有位
      IncrWrap 值增加1,大于255时,变成0.
      DecrWrap 值减少1,小于0时,变成255

好了,有了上述的介绍,再回到场景中,场景分为3个部分,蓝色的区域,黑色的区域和连接两个区域的门,
首先是门,负责显示玩家可以进入的场景,所以门主要的功能就是:把屏幕上对应位置的Ref值刷新为进入的场景的Ref值,本身不需要显示任何颜色;
然后是两个场景,分别有不同的Ref值(黑色场景的Ref为1,蓝色场景的Ref为2)。

下面开始具体的实现,以玩家处于蓝色场景,将要进入黑色场景为例,首先新建3个Surface Shader分别对应蓝色场景,黑色场景,门:

  • 蓝色场景是需要显示出来的,所以场景中的模板测试是需要通过的,即Comp Alawys,这样场景就可以显示出来;
//在SubShader中添加
        stencil
        {
            ref 2
            comp Always
        }

·

  • 黑色场景只需要在门中看到,其他的地方是不需要显示的,它的比较方式就是 Comp Equal.
//在SubShader中添加
        stencil
        {
            ref 1
            comp Equal
        }

·

  • 门需要刷新的Ref值就是黑色场景的Ref值.
//在SubShader中添加
        Zwrite off   //关闭写入深度,防止在深度测试中将门后的场景覆盖
        Cull off     //开启双面显示
        Colormask 0  //屏蔽颜色的输出
        stencil
        {
            Ref 1
            Comp Alawys
            Pass replace  //测试通过则将stencilBufferValue刷新为1
        }

到这一步,就实现了一个静态的效果:从蓝色场景看到门中的黑色场景。

1.png

接下来是动态的实现,穿越了门后,黑色场景全部显示,而蓝色场景则可以从门中看到:

  • 门shader的属性Properties中新增2个属性,方便在c#脚本中动态的赋值:
        //新增属性
        _RefValue("Ref",range(0,255)) = 0
        [Enum(UnityEngine.Rendering.CompareFunction)]_StencilComp("Stencil Comp",float) = 3
        //修改stencil
        stencil
        {
            ref [_RefValue]    //替换成修改后的属性
            comp [_StencilComp]   //替换成修改后的属性
            pass replace
        }
  • 场景shader的属性Properties新增一个属性
         //新增属性
        [Enum(UnityEngine.Rendering.CompareFunction)]_RefValue("Ref Valus",int) = 3
        stencil
        {
            ref 2
            comp [_RefValue]    //替换成新增属性
        }

  • 在c#脚本中动态的控制这些属性,关键代码如下:
if(cameraPostionInPortalSpace.z < -0.3) //表示在scene2这边
        {
            scene2Mat.SetInt("_RefValue", (int)CompareFunction.Always);
            scene1Mat.SetInt("_RefValue", (int)CompareFunction.Equal);
            portalMat.SetInt("_RefValue", 1);
        }
        else if (cameraPostionInPortalSpace.z >0.3) //表示在scene1这边
        {
            scene2Mat.SetInt("_RefValue", (int)CompareFunction.Equal);
            scene1Mat.SetInt("_RefValue", (int)CompareFunction.Always);
            portalMat.SetInt("_RefValue", 2);
        }
        else    //这里表示在门口附近,两边场景都需要显示
        {
            scene2Mat.SetInt("_RefValue", (int)CompareFunction.Always);
            scene1Mat.SetInt("_RefValue", (int)CompareFunction.Always);
        }

到这里就差不多啦~~~

git仓库:https://github.com/Looooooong/ShadersRoom

最后附上完整的shader:
·

Shader "ShadersRoom/StencilPortal" {
    Properties {
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _Glossiness ("Smoothness", Range(0,1)) = 0.5
        _Metallic ("Metallic", Range(0,1)) = 0.0

        _RefValue("Ref",range(0,255)) = 0
        [Enum(UnityEngine.Rendering.CompareFunction)]_StencilComp("Stencil Comp",float) = 3
    }
    SubShader {
        Tags { "RenderType"="Opaque" }
        LOD 200
        zwrite off
        cull off
        colormask 0


        stencil
        {
            ref [_RefValue]
            comp [_StencilComp]
            pass replace
        }

        CGPROGRAM
        // Physically based Standard lighting model, and enable shadows on all light types
        #pragma surface surf Standard fullforwardshadows

        // Use shader model 3.0 target, to get nicer looking lighting
        #pragma target 3.0

        sampler2D _MainTex;

        struct Input {
            float2 uv_MainTex;
        };

        half _Glossiness;
        half _Metallic;
        fixed4 _Color;


        void surf (Input IN, inout SurfaceOutputStandard o) {
            // Albedo comes from a texture tinted by color
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
            o.Albedo = c.rgb;
            // Metallic and smoothness come from slider variables
            o.Metallic = _Metallic;
            o.Smoothness = _Glossiness;
            o.Alpha = c.a;
        }
        ENDCG
    }
    FallBack "Diffuse"
}

Shader "ShadersRoom/Scene1" {
    Properties {
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _Glossiness ("Smoothness", Range(0,1)) = 0.5
        _Metallic ("Metallic", Range(0,1)) = 0.0
        [Enum(UnityEngine.Rendering.CompareFunction)]_RefValue("Ref Valus",int) = 1
    }
    SubShader {
        Tags { "RenderType"="Opaque" }
        LOD 200

        stencil
        {
            ref 1
            comp [_RefValue]
        }

        CGPROGRAM
        // Physically based Standard lighting model, and enable shadows on all light types
        #pragma surface surf Standard fullforwardshadows

        // Use shader model 3.0 target, to get nicer looking lighting
        #pragma target 3.0

        sampler2D _MainTex;

        struct Input {
            float2 uv_MainTex;
        };

        half _Glossiness;
        half _Metallic;
        fixed4 _Color;


        void surf (Input IN, inout SurfaceOutputStandard o) {
            // Albedo comes from a texture tinted by color
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
            o.Albedo = c.rgb;
            // Metallic and smoothness come from slider variables
            o.Metallic = _Metallic;
            o.Smoothness = _Glossiness;
            o.Alpha = c.a;
        }
        ENDCG
    }
    FallBack "Diffuse"
}

Shader "ShadersRoom/Scene2" {
    Properties {
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _Glossiness ("Smoothness", Range(0,1)) = 0.5
        _Metallic ("Metallic", Range(0,1)) = 0.0
        [Enum(UnityEngine.Rendering.CompareFunction)]_RefValue("Ref Valus",int) = 3
    }
    SubShader {
        Tags { "RenderType"="Opaque" }
        LOD 200

        stencil
        {
            ref 2
            comp [_RefValue]
        }

        CGPROGRAM
        // Physically based Standard lighting model, and enable shadows on all light types
        #pragma surface surf Standard fullforwardshadows

        // Use shader model 3.0 target, to get nicer looking lighting
        #pragma target 3.0

        sampler2D _MainTex;

        struct Input {
            float2 uv_MainTex;
        };

        half _Glossiness;
        half _Metallic;
        fixed4 _Color;


        void surf (Input IN, inout SurfaceOutputStandard o) {
            // Albedo comes from a texture tinted by color
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
            o.Albedo = c.rgb;
            // Metallic and smoothness come from slider variables
            o.Metallic = _Metallic;
            o.Smoothness = _Glossiness;
            o.Alpha = c.a;
        }
        ENDCG
    }
    FallBack "Diffuse"
}

你可能感兴趣的:(ShadersRoom - Stencil Buffer)