http://blog.csdn.net/u011047171/article/details/46928463

Stencil Buffer&Stencil Test

在开始前先吐槽下unity的官方文档,说实话关于stencil ,官方文档真的是可以不要了,除了记流水账般解释了下各个参数的作用,作为例子的shader也是让人一头雾水,整个文档看下来,你发觉stencil是用来干嘛的,怎么操作,仍然不知道。好在unity的shaderlab 和D3D,OpenGL等shader语言是一致的,还可以从它们的相关解释来了解stencil。关于unity的stencil,我对它的理解还是不够深入,unity是如何初始化stencilbuffer的值还是知之甚少。下面就是权当抛砖引玉了,如有错误,欢迎高手留言指正,不甚感激。

模板测试概要

言归正传,stencil与颜色缓冲区和深度缓冲区类似,模板缓冲区可以为屏幕上的每个像素点保存一个无符号整数值(通常的话是个8位整数)。这个值的具体意义视程序的具体应用而定。在渲染的过程中,可以用这个值与一个预先设定的参考值相比较,根据比较的结果来决定是否更新相应的像素点的颜色值。这个比较的过程被称为模板测试。模板测试发生在透明度测试(alpha
test)之后,深度测试(depth test)之前。如果模板测试通过,则相应的像素点更新,否则不更新。图形渲染管线中,基于单个像素的测试操作的顺序如下图

模板测试语法

一般来说,stencil完整语法格式如下:

[csharp] view
plain
 copy

print?

  1. stencil{
  2. Ref referenceValue
  3. ReadMask  readMask
  4. WriteMask writeMask
  5. Comp comparisonFunction
  6. Pass stencilOperation
  7. Fail stencilOperation
  8. ZFail stencilOperation
  9. }

Ref

[csharp] view
plain
 copy

print?

  1. Ref referenceValue

Ref用来设定参考值referenceValue,这个值将用来与模板缓冲中的值进行比较。referenceValue是一个取值范围位0-255的整数。

ReadMask

[csharp] view
plain
 copy

print?

  1. ReadMask  readMask

ReadMask 从字面意思的理解就是读遮罩,readMask将和referenceValue以及stencilBufferValue进行按位与(&)操作,readMask取值范围也是0-255的整数,默认值为255,二进制位11111111,即读取的时候不对referenceValue和stencilBufferValue产生效果,读取的还是原始值。

WriteMask

[csharp] view
plain
 copy

print?

  1. WriteMask writeMask

WriteMask是当写入模板缓冲时进行掩码操作(按位与【&】),writeMask取值范围是0-255的整数,默认值也是255,即当修改stencilBufferValue值时,写入的仍然是原始值。

Comp

[csharp] view
plain
 copy

print?

  1. Comp comparisonFunction

Comp是定义参考值(referenceValue)与缓冲值(stencilBufferValue)比较的操作函数,默认值:always

Pass

[csharp] view
plain
 copy

print?

  1. Pass stencilOperation

Pass是定义当模板测试(和深度测试)通过时,则根据(stencilOperation值)对模板缓冲值(stencilBufferValue)进行处理,默认值:keep

Fail

[csharp] view
plain
 copy

print?

  1. Fail stencilOperation

Fail是定义当模板测试(和深度测试)失败时,则根据(stencilOperation值)对模板缓冲值(stencilBufferValue)进行处理,默认值:keep

ZFail

ZFail是定义当模板测试通过而深度测试失败时,则根据(stencilOperation值)对模板缓冲值(stencilBufferValue)进行处理,默认值:keep

Comp,Pass,Fail 和ZFail将会应用给背面消隐的几何体(只渲染前面的几何体),除非Cull Front被指定,在这种情况下就是正面消隐的几何体(只渲染背面的几何体)。你也可以精确的指定双面的模板状态通过定义CompFront,PassFront,FailFront,ZFailFront(当模型为front-facing geometry使用)和ComBack,PassBack,FailBack,ZFailBack(当模型为back-facing
geometry使用)

模板测试判断依据

和深度测试一样,在unity中,每个像素的模板测试也有它自己一套独立的依据,具体公式如下:

if(referenceValue&readMask comparisonFunction stencilBufferValue&readMask)

通过像素

else

抛弃像素

在这个公式中,主要分comparisonFunction的左边部分和右边部分

referenceValue是有Ref来定义的,这个是由程序员来定义的,readMask是模板值读取掩码,它和referenceValue进行按位与(&)操作作为公式左边的结果,默认值为255,即按位与(&)的结果就是referenceValue本身。

stencilBufferValue是对应位置当前模板缓冲区的值,同样与readMask做按位掩码与操作,结果做为右边的部分。

comparisonFunction比较操作通过Comp命令定义,公式左右两边的结果将通过它进行判断,其取值及其意义如下面列表所示。

   
Greater

相当于“>”操作,即仅当左边>右边,模板测试通过,渲染像素

GEqual

相当于“>=”操作,即仅当左边>=右边,模板测试通过,渲染像素

Less

相当于“<”操作,即仅当左边<右边,模板测试通过,渲染像素

LEqual

相当于“<=”操作,即仅当左边<=右边,模板测试通过,渲染像素

Equal

相当于“=”操作,即仅当左边=右边,模板测试通过,渲染像素

NotEqual

相当于“!=”操作,即仅当左边!=右边,模板测试通过,渲染像素

Always 不管公式两边为何值,模板测试总是通过,渲染像素


Never 不敢公式两边为何值,模板测试总是失败 ,像素被抛弃


模板缓冲值的更新

在上一步的模板测试之后,无论模板测试通过与否,都要对模板进行相应的更新。具体到怎么更新,则由程序员自己定义。上面关于模板缓冲语法中,Pass,Fail,ZFail等命令就是根据不同判断条件对模板缓冲区的值(stencilBufferValue)进行更新的操作,这些命令取值(stencilOperation)的类型及意义如下面列表所示:

   
Keep

保留当前缓冲中的内容,即stencilBufferValue不变。
Zero

将0写入缓冲,即stencilBufferValue值变为0。
Replace 将参考值写入缓冲,即将referenceValue赋值给stencilBufferValue。


IncrSat

stencilBufferValue加1,如果stencilBufferValue超过255了,那么保留为255,即不大于255。
DecrSat

stencilBufferValue减1,如果stencilBufferValue超过为0,那么保留为0,即不小于0。
Invert

将当前模板缓冲值(stencilBufferValue)按位取反
IncrWrap

当前缓冲的值加1,如果缓冲值超过255了,那么变成0,(然后继续自增)。
DecrWrap

当前缓冲的值减1,如果缓冲值已经为0,那么变成255,(然后继续自减)  。

在更新模板缓冲值的时候,也有writeMask进行掩码操作,用来对特定的位进行写入和屏蔽,默认值为255(11111111),即所有位数全部写入,不进行屏蔽操作。

举个如下的例子:

[csharp] view
plain
 copy

print?

  1. stencil{
  2. Ref 2
  3. Comp always
  4. Pass replace
  5. }

在上面的代码中,第一行Ref 2这行将referenceValue定义为2;

第二行中,Comp命令后的参数是always,此时我们不管stencilBufferValue为多少,模板测试都是成功通过的;

而第三行中,Pass replace的意思是,当模板测试通过则将referenceValue替换给stencilBufferValue,此时

stencilBufferValue值为2,因此上面的例子功能相当于将stencilBufferValue刷新为2;

小结

上面说了这么多,主要的重点如下

  • 使用模板缓冲区最重要的两个值:当前模板缓冲值(stencilBufferValue)和模板参考值(referenceValue)
  • 模板测试主要就是对这个两个值使用特定的比较操作:Never,Always,Less ,LEqual,Greater,Equal等等。
  • 模板测试之后要对模板缓冲区的值(stencilBufferValue)进行更新操作,更新操作包括:Keep,Zero,Replace,IncrSat,DecrSat,Invert等等。
  • 模板测试之后可以根据结果对模板缓冲区做不同的更新操作,比如模板测试成功操作Pass,模板测试失败操作Fail,深度测试失败操作ZFail,还有正对正面和背面精确更新操作PassBack,PassFront,FailBack等等。

实例操作

上面主要是理论知识,下面将通过一个实例大概了解下stencil的简单应用,使用stencil缓冲用来限制渲染区域,效果如下:

这个实例需要两个shader实现,如上面的那个用来限制区域的box所使用的shader中的关键代码:

[csharp] view
plain
 copy

print?

  1. ColorMask 0
  2. ZWrite Off
  3. Stencil{
  4. Ref 1
  5. Comp Always
  6. Pass Replace
  7. }

上面这段代码中,ColorMask 0作用是屏蔽颜色的输出,即不输出颜色到屏幕。ZWrite Off用来关闭深度写入,防止深度测试中后面的角色的像素被剔除掉;在stencil中 Ref 1将referenceValue设置成1,Comp Always 保证模板测试始终通过,Pass Replace 操作则将stencilBufferValue刷新为1;即这段代码的功能是在屏幕上对应模型的位置不输入任何颜色,而将对应位置的模板缓冲值刷新为1;

接下来需要在角色使用的shader中添加如下关键代码:

[csharp] view
plain
 copy

print?

  1. Stencil {
  2. Ref 1
  3. Comp Equal
  4. }

,

上面这段代码中,Ref 1将referenceValue设置成1,在接下来的一行代码中,Comp Equal的意思是,如果referenceValue=stencilBufferValue,则模板测试通过,渲染像素,否则抛弃;在这个例子中,由于屏幕中的像素默认的模板值(stencilBufferValue)为0(我猜的,貌似是正确的哈)而参考值referenceValue为1,,所以正常情况下使用这个shader的模型是不显示的,但是在使用了第一个shader的box区域,由于stencilBufferValue被刷新为1,所以在这个区域中,角色是能够显示的。

本例完整代码如下:

[csharp] view
plain
 copy

print?

  1. Shader "Custom/UnlitStencilMaskVF" {
  2. SubShader {
  3. Tags { "RenderType"="Opaque" "Queue"="Geometry-1"}
  4. CGINCLUDE
  5. struct appdata {
  6. float4 vertex : POSITION;
  7. };
  8. struct v2f {
  9. float4 pos : SV_POSITION;
  10. };
  11. v2f vert(appdata v) {
  12. v2f o;
  13. o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
  14. return o;
  15. }
  16. half4 frag(v2f i) : SV_Target {
  17. return half4(1,1,0,1);
  18. }
  19. ENDCG
  20. Pass {
  21. ColorMask 0
  22. ZWrite Off
  23. Stencil
  24. {
  25. Ref 1
  26. Comp Always
  27. Pass Replace
  28. }
  29. CGPROGRAM
  30. #pragma vertex vert
  31. #pragma fragment frag
  32. ENDCG
  33. }
  34. }
  35. }
[csharp] view
plain
 copy

print?

  1. Shader "Custom/UnlitStencilVF" {
  2. Properties {
  3. _MainTex ("Base (RGB)", 2D) = "white" {}
  4. }
  5. SubShader {
  6. Tags { "Queue" = "Geometry""RenderType"="Opaque" }
  7. LOD 100
  8. Pass {
  9. Stencil {
  10. Ref 1
  11. Comp Equal
  12. }
  13. CGPROGRAM
  14. #pragma vertex vert
  15. #pragma fragment frag
  16. #pragma multi_compile_fog
  17. #include "UnityCG.cginc"
  18. struct appdata_t {
  19. float4 vertex : POSITION;
  20. float2 texcoord : TEXCOORD0;
  21. };
  22. struct v2f {
  23. float4 vertex : SV_POSITION;
  24. half2 texcoord : TEXCOORD0;
  25. UNITY_FOG_COORDS(1)
  26. };
  27. sampler2D _MainTex;
  28. float4 _MainTex_ST;
  29. v2f vert (appdata_t v)
  30. {
  31. v2f o;
  32. o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
  33. o.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);
  34. UNITY_TRANSFER_FOG(o,o.vertex);
  35. return o;
  36. }
  37. fixed4 frag (v2f i) : SV_Target
  38. {
  39. fixed4 col = tex2D(_MainTex, i.texcoord);
  40. UNITY_APPLY_FOG(i.fogCoord, col);
  41. UNITY_OPAQUE_ALPHA(col.a);
  42. return col;
  43. }
  44. ENDCG
  45. }
  46. }
  47. }

UnityShader实例09:Stencil Buffer&Stencil Test的更多相关文章

  1. UnityShader学习笔记- Stencil Buffer

    模板测试(Stencil Test)是现代渲染流水线的一环,其中涉及到的就是模板缓冲(Stencil Buffer),模板缓冲可以用来制作物体的遮罩.轮廓描边.阴影.遮挡显示等等效果 目录 Stenc ...

  2. 【D3D12学习手记】4.3.8 Create the Depth/Stencil Buffer and View

    我们现在需要创建深度/模板缓冲区. 如§4.1.5所述,深度缓冲区只是一个2D纹理,用于存储最近的可见对象的深度信息(如果使用模板(stencil),则也会存储模板信息). 纹理是一种GPU资源,因此 ...

  3. Stencil Buffer

    刚在草稿箱里发现了这篇充满特色的好日志.发表之. ------------------吃货的分割线---------------------------------------- Stencil Bu ...

  4. 在DirectX11下用Stencil Buffer绘制可视化Depth Complexity

    这是一道在<Introduction to 3D Game Programming with DirectX 11>上的练习题. 要求把某个像素点上的Depth Complexity(深度 ...

  5. windows API下的模板缓冲(stencil buffer)

    在windows API搭建的OpenGL窗口中使用模板缓冲,需要在像素格式描述表中设置stencil buffer位宽为8,这样窗口会自动生成stencil buffer,然后可以在opengl环境 ...

  6. depth/stencil buffer的作用 ----------理解模板缓存 opengl

    在D3D11中,有depth/stencil buffer,它们和framebuffer相对应,如下图所示,framebuffer中一个像素,有相对应的depth buffer和stencil buf ...

  7. Directx11教程(50) 输出depth/stencil buffer的内容

    原文:Directx11教程(50) 输出depth/stencil buffer的内容      有时候,我们需要查看depth/stencil buffer的内容,比如上一章中,我们要查看sten ...

  8. Directx11教程(48) depth/stencil buffer的作用

    原文:Directx11教程(48) depth/stencil buffer的作用      在D3D11中,有depth/stencil buffer,它们和framebuffer相对应,如下图所 ...

  9. UnityShader实例13:屏幕特效之均值模糊(Box Blur)

    均值模糊(Box Blur) 概述 因为公司手游项目需求.须要一个适合手机平台的模糊效果,同一时候须要开放一个參数便于调节模糊值.我首先想到的就是ps里面的均值模糊. 查资料能够知道均值模糊是一种高速 ...

随机推荐

  1. SAP 已经有17个模块

    SAP模块清单: 传统五大郎: MM,SD,PP,FI 财务会计CO 管理会计 +QM 质量管理 (制造业用的不少)+ABAP.BASIS.BW BI商务智能的组件之一CRM 客户管理SRM 供应商管 ...

  2. python调试利器:最直观简洁的错误日志

    #!/usr/bin/env python # -*- coding: utf-8 -*- # @Date : 2018-08-30 17:12:27 # @Author : Sheldon (thi ...

  3. selenium WebDriverException: Message: unknown error: DevToolsActivePort file doesnt exist

    在centos中使用无头chrome报以下错误 selenium.common.exceptions.WebDriverException: Message: unknown error: DevTo ...

  4. 剑指Offer:链表中环的入口节点【23】

    剑指Offer:链表中环的入口节点[23] 题目描述 给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null. 题目分析 第一步确定链表中是否包含环,怎么确定呢?我们定义两个指针橙和 ...

  5. Android tab页制作

    全屏启动页 activity_launcher <?xml version="1.0" encoding="utf-8"?> <Relativ ...

  6. MVC常见错误记录

    1 找到了多个与名为“Home”的控制器匹配的类型.如果为此请求(“{controller}/{action}/{id}”)提供服务的路由没有指定命名空间来搜索匹配此请求的 根项目下的RouteCon ...

  7. L97

    We are young. So let's set the world on fire. We can burn brighter than the sun.我们是青年,让我们点亮世界,释放生命,胜 ...

  8. WPF 后台触发 Validate UI‘s Element

    wpf中有validateRule类, 用于界面元素的验证, 如何后台去控制validateRule呢? 1. UI层要binding写好的ValidateRule,分为Binding和MultiBi ...

  9. MQTT 客户端源码分析

    参看:逍遥子_mosquitto源码分析系列 参看:MQTT libmosquitto源码分析 参看:Mosquitto学习笔记 一.目录结构 首先我们还是来看一下 mosquitto-1.4.14 ...

  10. springboot 1.5.x中的动态切换日志级别

    logback是一套日志框架,由log4j的优化版,由同一个作者开发,在速度和性能上都超过其他日志框架,再结合slf4j,已成为当前最流行的日志框架. 一.springboot中使用logback s ...