stencil案例分析

案例1:描边

原文地址

Shader "Unlit/StentilOutline"
{
	Properties
	{
		_MainTex ("Texture", 2D) = "white" {}
		_Outline("OutLine",range(0,1))=0
	}
	SubShader
	{
		Tags { "RenderType"="Opaque" }
		LOD 100
        Stencil {
             Ref 0          //0-255
             Comp Equal     //default:always
             Pass IncrSat   //default:keep
             Fail keep      //default:keep
             ZFail keep     //default:keep
        }

		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 fixed4(1,1,0,1);
                return col;
			}
			ENDCG
		}

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            // make fog work
            #pragma multi_compile_fog
            
            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float4 normal: NORMAL;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                UNITY_FOG_COORDS(1)
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
			fixed _Outline;
            
            v2f vert (appdata v)
            {
                v2f o;
                o.vertex=v.vertex+normalize(v.normal)*_Outline;
                o.vertex = UnityObjectToClipPos(o.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 fixed4(1,1,1,1);
            }
            ENDCG
        }
	}
}


这个案例是拷贝过来的,用来做描边功能
stencil的默认值是0,而buffer的值在当前帧结束前是不清除的,所以它可以跨越不同的shader与pass。Stencil结构写在Subshader中,那么下面的所有pass中的stencil test都按此运行。理想环境下,第一个pass渲染前屏幕上所有像素的stencil值都是0,在该pass fragment shader片段作色器结束后,所有进行了渲染的像素stencil值都变为了1。
要点:stencil写在pass外面,每个pass都会执行stencil一遍

案例2:

效果:

在这里插入图片描述
这是用两个shader来实现,球的shader跟两个胶囊体
代码如下:
胶囊体(先渲染Geometry+1):

Shader "SoulCoder/Mask" {
    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
        }
    }
    }

球体(后渲染Geometry+2):

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

        ColorMask RGB
        Cull Front
        ZTest Always
        Stencil {
            Ref 0
            Comp Equal 
        }

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


分析:第一个shader因为ColorMask 0,所以不输出颜色,然后把其渲染的范围模板值写入1
第二个shader的ref值为0,因为上一个shader pass完后,胶囊体的范围缓冲区的ref是1,所以球体跟胶囊体公共的区域,ref1不等于ref0,公共区域不满足Comp Equal ,所以丢弃片元,但是背景是ref0,故显示透明。球体除胶囊体范围的区域,ref0,跟背景的ref0相等,故全部通过测试,球体渲染。

案例3:

原文地址
效果:
在这里插入图片描述在这里插入图片描述
这是用一个蒙板来控制角色的显示,目的是让在想显示的区域显示角色,在不想让他显示的区域不显示,有点像裁剪。代码如下:
蒙版代码:

// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

Shader "Custom/UnlitStencilMaskVF" {
	SubShader {
        Tags { "RenderType"="Opaque" "Queue"="Geometry-1"}
 
        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 {
           
        ColorMask 0
	    ZWrite Off
	
			Stencil
		{
			Ref 1
			Comp Always
			Pass Replace
		}
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            ENDCG
        }        
    } 
}


角色的代码:

Shader "Custom/UnlitStencilVF" {
	Properties {
	_MainTex ("Base (RGB)", 2D) = "white" {}
}
 
SubShader {
	Tags { "Queue" = "Geometry""RenderType"="Opaque" }
	LOD 100
 
	Pass { 
	
		Stencil {
                Ref 1
      		Comp Equal
                } 
		CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			#pragma multi_compile_fog
			
			#include "UnityCG.cginc"
 
			struct appdata_t {
				float4 vertex : POSITION;
				float2 texcoord : TEXCOORD0;
				
			};
 
			struct v2f {
				float4 vertex : SV_POSITION;
				half2 texcoord : TEXCOORD0;
			
				UNITY_FOG_COORDS(1)
			};
 
			sampler2D _MainTex;
			float4 _MainTex_ST;
			
			v2f vert (appdata_t v)
			{
				v2f o;
				o.vertex = UnityObjectToClipPos(v.vertex);
				o.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);
								UNITY_TRANSFER_FOG(o,o.vertex);
				return o;
			}
			
			fixed4 frag (v2f i) : SV_Target
			{
				fixed4 col = tex2D(_MainTex, i.texcoord);
				UNITY_APPLY_FOG(i.fogCoord, col);
				UNITY_OPAQUE_ALPHA(col.a);
			
				return col;
			}
		ENDCG
	}
}
 
}


分析:1.首先看遮罩的queue,先渲染遮罩蒙版shader,(蒙版的queue=Geometry,角色的queue=Geometry+1),
ColorMask 0意味着不输出颜色,然后关闭zwrite,防止后面的角色因为深度原因被剔除掉,然后ref 1 Comp Always是把模板的引用值设置为1,为了之后在渲染角色的时候,让ref=1的部分通过模版测试。Pass Replace此时模板缓冲区ref全部替换为1
2.然后渲染角色shader,在检测渲染状态的时候,模板测试过程中 Ref 1 Comp Equal,意思是只有当引用值跟模板缓冲区的值都为1,测试通过,此时因为在遮罩区域的ref为1,遮罩外面全部是0,所以,只有遮罩内的角色像素才能显示出来

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值