2020-08-12
1、简介
在Unity中一个像素会有多个缓存信息,模版缓存(其实Stencil不应该被翻译成模版,但网上都这么写)是其中之一。模板缓冲区可以为屏幕上的每个像素点保存一个无符号整数值,通过一些比较来改变当前像素区域模版缓冲的值,从而改变深度关系,似乎UGUI的Mask也是通过其实现的,延迟光照中Stencil有一些限制,具体看unity的文档,这里只介绍正向光照中的使用。
Stencil所在的渲染管线中的顺序位置:
Stencil的所有参数如下:
Stencil { Ref 1//Reference Value ReadMask 255 WriteMask 255 Comp Always //Comparison Function Pass Replace Fail Keep ZFail Replace }
Ref 就是参考值,当参数允许赋值时,会把参考值赋给当前像素
ReadMask 对当前参考值和已有值进行mask操作,默认值255,一般不用
WriteMask 写入Mask操作,默认值255,一般不用
Comp 比较方法。是拿Ref参考值和当前像素缓存上的值进行比较。默认值always
- Greater - 大于
- GEqual - 大于等于
- Less - 小于
- LEqual - 小于等于
- Equal - 等于
- NotEqual - 不等于
- Always - 永远通过
- Never - 永远通不过
Pass 当模版测试和深度测试都通过时,进行处理
Fail 当模版测试和深度测试都失败时,进行处理
ZFail 当模版测试通过而深度测试失败时,进行处理
pass,Fail,ZFail都属于Stencil操作,他们参数统一如下:
- Keep 保持(即不把参考值赋上去,直接不管)
- Zero 归零
- Replace 替换(拿参考值替代原有值)
- IncrSat 值增加1,但不溢出,如果到255,就不再加
- DecrSat 值减少1,但不溢出,值到0就不再减
- Invert 反转所有位,如果1就会变成254
- IncrWrap 值增加1,会溢出,所以255变成0
- DecrWrap 值减少1,会溢出,所以0变成255
2、实践
下面结合案例,说一下Stencil的使用
现在有一个球和面片,通过Stencil模版测试把stencil值一致的物体渲染为蓝色。
为了更直观使用了两个shader,面片是Panel,球是Sphere。
注意:
球如果是优先渲染的话,会导致深度值无法比较。所以本案例:面片先渲染,并且设置好stencil的值;球后渲染,并且判断stencil的值。
文件结构:
Shader "Stencil/Panel" { Properties { _MainTex ("Texture", 2D) = "white" {} } SubShader { Tags{ "RenderType" = "Opaque" "Queue" = "Geometry" } Stencil { Ref 1 Comp Always Pass Replace } Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; }; struct v2f { float2 uv : TEXCOORD0; 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); return o; } fixed4 frag (v2f i) : SV_Target { return fixed4(1,1,1,1); } ENDCG } } } MaskTest2
Stencil里,比较条件设置为总通过,通过操作设置为替换Ref值;
这样球部分的像素Stencil值就是1
Stencil { Ref 1 Comp Always Pass Replace }
Shader "Stencil/Sphere" { Properties { _MainTex ("Texture", 2D) = "white" {} } SubShader { Tags{ "RenderType" = "Opaque" "Queue" = "Geometry+1" } Stencil { Ref 1 Comp equal } Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; }; struct v2f { float2 uv : TEXCOORD0; 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); return o; } fixed4 frag (v2f i) : SV_Target { return fixed4(0,0,1,1); } ENDCG } } }
Stencil里,比较条件是和Ref值一致时,才会执行渲染。
否则跳出
Stencil { Ref 1 Comp equal }
蓝色是片段shader里写好的
fixed4 frag (v2f i) : SV_Target { return fixed4(0,0,1,1); }