官方文档地址:
https://docs.unity3d.com/Manual/SL-Stencil.html
参考博客:
http://blog.csdn.net/onafioo/article/details/53943264
http://blog.csdn.net/u013833399/article/details/47340447
(因表述的内容都大致一样,一些内容直接引用自原作者)
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,面片是shader1,球是shader2
球肯定是优先渲染,并且设置好stencil的值,面片后渲染,并且判断stencil的值
文件结构:
MaskTest2是球的shader
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Shader "Unlit/MaskTest2" { 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 } } }
Stencil里,比较条件设置为总通过,通过操作设置为替换Ref值
这样球部分的像素Stencil值就是1
Stencil { Ref 1 Comp Always Pass Replace }
MaskTest1是面片的Shader
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Shader "Unlit/MaskTest1" { 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); }
这样就可以达到上述效果。
测试工程文件下载:http://files.cnblogs.com/files/hont/StencilMaskTest.zip