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. 【python】使用python写windows服务

    背景 运维windows服务器的同学都知道,windows服务器进行批量管理的时候非常麻烦,没有比较顺手的工具,虽然saltstack和ansible都能够支持windows操作,但是使用起来总感觉不 ...

  2. 开发的第一个PHP扩展

    下载php源码php-5.4.23.tar.gz,解压,进入/home/hubo/php-5.4.23/ext/扩展目录 wget http://cn2.php.net/get/php-5.4.23. ...

  3. Java for LeetCode 111 Minimum Depth of Binary Tree

    Given a binary tree, find its minimum depth. The minimum depth is the number of nodes along the shor ...

  4. 通道(Channel)的原理获取

    通道表示打开到 IO 设备(例如:文件.套接字)的连接.若需要使用 NIO 系统,需要获取用于连接 IO 设备的通道以及用于容纳数据的缓冲区.然后操作缓冲区,对数据进行处理.Channel 负责传输, ...

  5. smokeping高级配置

    摘自: http://mayulin.blog.51cto.com/1628315/514367 自定义报警 http://www.cnblogs.com/thatsit/p/6395506.html

  6. 大数据初级笔记二:Hadoop入门之Hadoop集群搭建

    Hadoop集群搭建 把环境全部准备好,包括编程环境. JDK安装 版本要求: 强烈建议使用64位的JDK版本,这样的优势在于JVM的能够访问到的最大内存就不受限制,基于后期可能会学习到Spark技术 ...

  7. Could not load the "xxx.png" image referenced from a nib in the bundle with identifier "com.xxxx"

    打印台logs:  Could not load the "xxx.png" image referenced from a nib in the bundle with iden ...

  8. BZOJ-3881:Divljak (AC自动机+DFS序+树链求并+树状数组)

    Alice有n个字符串S_1,S_2...S_n,Bob有一个字符串集合T,一开始集合是空的. 接下来会发生q个操作,操作有两种形式: “1 P”,Bob往自己的集合里添加了一个字符串P. “2 x” ...

  9. P2936(BZOJ3396) [USACO09JAN]全流Total Flow[最大流]

    题 裸题不多说,在网络流的练习题里,你甚至可以使用暴力. #include<bits/stdc++.h> using namespace std; typedef long long ll ...

  10. [JSOI 2018] 潜入行动

    [题目链接] https://www.lydsy.com/JudgeOnline/problem.php?id=5314 [算法] 考虑dp , 用f[i][j][0 / 1][0 / 1]表示以i为 ...