# 无顺序摘录
[toc]
# 模板测试渲染管线
片元着色器处理完之后到帧缓冲输出之间,有一个逐片元操作部分,这个部分分为很多个测试。模板测试就在其中
Pixel Ownership Test: 控制像素显示权限
Scissor Test: 裁剪测试,可以控制渲染那部分,比如只渲染左上角 / 右下角
Aplha Test: 根据 Alpha 值进行偏远裁剪
模板测试
深度测试
透明度混合 (半透)
逐片元阶段不可编程,但高度可配置
# 模板测试是什么
从逻辑上理解
1 2 3 4 5 if (referenceValue&readMask comparisonFunction stencilBufferValue&readMask){ 通过像素 }else { 抛弃像素 }
referenceValue: 当前片元参考值
stencilBufferValue: 模板缓冲区里的参考值
comparisonFunction: 将上面这两个值进行比较,通过则通过,失败则抛弃
# 模板缓冲区
模板缓冲区可以为屏幕上的每个像素点保存一个无符号整数值 (通常是个 8 位整数)。
在渲染中,可以用这个值与一个预先设定好的参考值进行比较,根据比较的结果来决定是否更新相应的像素点的颜色值。
# 语法
1 2 3 4 5 6 7 8 9 stencil{ Ref referenceValue ReadMask readMask WriteMask writeMask Comp compairsonFunction Pass stencilOperation Fail stencilOperation ZFail stencilOperation }
其中 ComparisonFunction 包括:
Greater 相当于 ">" 操作,即仅当左边 > 右边,通过
GEqual 相当于 ">="
Less 相当于 "<"
LEqual 相当于 "<="
Equal 相当于 "="
NotEqual 相当于 "!="
Always 总是通过
Never 永远不通过
其中 stencilOperation 包括:
Keep 保留当前缓冲区内容,即 stencilBufferValue 不变
Zero 将 0 写入缓冲区,即 stencilBufferValue 值变为 0
Replace 将参考值写入缓冲区,即将 referenceValue 赋值给 stencilBuffer
IncrSat stencilBufferValue 加 1, 如果 stencilBufferValue 超过 255 了,那么不再增加
DesrSat: stencilBuffer 减一,如果低于 0, 不变
Invert: stencilBuffer 按位取反
IncrWrap: 缓冲值加一,如果缓冲值超过 255,那么变成 0,
DescrWrap: 与上面相反
# 以 Minions Art 的一个效果作为例子
可以发现,有些地方是被剔除掉的,就是可以实现一个类似于传送门的效果 那么他是怎么实现的呢?在渲染管线中,有一个标记是专门供模板测试来使用的标记.
这个标记是放在像素上的
然后模板测试都是针对于这个标记来进行展开的。比如上面的实现
# 常驻标记 (一般作为传送门的门口)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 Shader "FX/StencilMask" { Properties{ _ID("Mask ID" , Int) = 1 } SubShader{ Tags{ "RenderType" = "Opaque" "Queue" = "Geometry+1" } ColorMask 0 ZWrite off Stencil{ Ref[_ID] Comp always Pass replace } Pass{ 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 ,1 ,1 ); } ENDCG } } }
我们可以看到上面主要分为几点
把 ColorMask 置为 0, 这样但凡经过该 Shader 渲染的都不输出颜色
关闭 ZBuffer, 没有深度影响
其中
1 2 3 4 5 Stencil{ Ref[_ID] Comp always Pass replace }
这一句是标准的模板测试使用的方法,我们看愿代码可以看到声明了一个_ID 的变量,这个变量就是我们要写入 模板位
中的值 Ref
代表了要拿哪个变量和模板位中的值进行比较
Comp
代表了比较方式,always 是总是通过
Pass
通过后执行 replace, 即替换 所以这句话的意思就是把 _ID的值
作为当前像素点的值写入缓存
# 可显示部分
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 Shader "Toon/Lit StencilMask" { Properties { _Color ("Main Color" , Color) = (0.5 ,0.5 ,0.5 ,1 ) _MainTex ("Base (RGB)" , 2 D) = "white" {} _Ramp ("Toon Ramp (RGB)" , 2 D) = "gray" {} _ID("Mask ID" , Int) = 1 } SubShader { Tags { "RenderType" ="Opaque" "Queue" = "Geometry+2" } LOD 200 Stencil { Ref [_ID] Comp equal } CGPROGRAM #pragma surface surf ToonRamp sampler2D _Ramp; #pragma lighting ToonRamp exclude_path:prepass inline half4 LightingToonRamp (SurfaceOutput s, half3 lightDir, half atten) { #ifndef USING_DIRECTIONAL_LIGHT lightDir = normalize (lightDir); #endif half d = dot (s.Normal, lightDir)*0.5 + 0.5 ; half3 ramp = tex2D (_Ramp, float2 (d,d)).rgb; half4 c; c.rgb = s.Albedo * _LightColor0.rgb * ramp * (atten * 2 ); c.a = 0 ; return c; } sampler2D _MainTex; float4 _Color; struct Input { float2 uv_MainTex : TEXCOORD0; }; void surf (Input IN, inout SurfaceOutput o) { half4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color; o.Albedo = c.rgb; o.Alpha = c.a; } ENDCG } Fallback "Diffuse" }
关注和模板缓冲区相关的部分
1 2 3 4 Stencil { Ref [_ID] Comp equal }
这句是代表以_ID 为参考值,与缓存内的模板值对比,如果想等,则允许渲染,否则丢弃
# 模板测试扩展