Stencil buffer
模版缓冲(stencil buffer)或印模缓冲,是在OpenGL三维绘图等计算机图像硬件中常见的除颜色缓冲、像素缓冲、深度缓冲之外另一种数据缓冲。词源模版(stencil)是指一种印刷技术,通常以蜡纸或者钢板印刷文字或图形;区别于模板(template),用木板为外形修剪的依据来复制形状;模版(stencil)是指印模,而模板(template)主要是指形模。模版缓冲是以像素为单位的,整数数值的缓冲,通常给每个像素分配一个字节长度的数值。深度缓冲与模版缓冲经常在图形硬件的随机存取内存(RAM)中分享相同的区域。
模板缓冲所在的是整个流水线的最后一个阶段,这个阶段的流水线如下
最简单的情况,模版缓冲被用于限制渲染的区域。更多高级应用会利用深度缓冲与模版缓冲的在图形渲染流水线中的强关联关系。例如,模版的数值可以按每个像素是否通过深度测试来自动增加或减少。
简单组合使用深度测试与模版修饰符可以使得大量的本来需要多次渲染过程的效果(例如阴影、外形的绘制或复合几何图元(Geometric primitive)的交叉部分的高光处理)可以简单实现,因此减轻了图形硬件的负担。
最典型的应用是给三维图像加阴影。也用于平面反射。
其它渲染的技术,例如,视口渲染(portal rendering),利用模版缓冲作其它用途。例如,它可以被用于查找被视口遮蔽的屏幕区域然后重新正确渲染这些像素点。
模版缓冲与其修饰符可以通过OpenGL或Direct3D的应用程序编程接口(API)来访问。
常用的比较操作
| |
Greater | Only render pixels whose reference value is greater than the value in the buffer. |
GEqual | Only render pixels whose reference value is greater than or equal to the value in the buffer. |
Less | Only render pixels whose reference value is less than the value in the buffer. |
LEqual | Only render pixels whose reference value is less than or equal to the value in the buffer. |
Equal | Only render pixels whose reference value equals the value in the buffer. |
NotEqual | Only render pixels whose reference value differs from the value in the buffer. |
Always | Make the stencil test always pass. |
Never | Make the stencil test always fail. |
模版像素操作
| |
Keep | Keep the current contents of the buffer. |
Zero | Write 0 into the buffer. |
Replace | Write the reference value into the buffer. |
IncrSat | Increment the current value in the buffer. If the value is 255 already, it stays at 255. |
DecrSat | Decrement the current value in the buffer. If the value is 0 already, it stays at 0. |
Invert | Negate all the bits. |
IncrWrap | Increment the current value in the buffer. If the value is 255 already, it becomes 0. |
DecrWrap | Decrement the current value in the buffer. If the value is 0 already, it becomes 255. |
Zfail
What to do with the contents of the buffer if the stencil test passes, but the depth test fails. Default: keep.
例子
下面是Unity文档里的例子,文档有点错误,按照文档的代码是写不出对应的结果的。
首先在场景里放一个红球,Shader如下
Shader "Custom/RedStencil" {
SubShader {
Tags { "RenderType"="Opaque" "Queue"="Geometry"}
Pass {
Stencil {
Ref 2
Comp always
Pass replace
ZFail decrWrap
}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
struct appdata {
float4 vertex : POSITION;
};
struct v2f {
float4 pos : SV_POSITION;
};
v2f vert(appdata v) {
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
return o;
}
half4 frag(v2f i) : SV_Target {
return half4(1,0,0,1);
}
ENDCG
}
}
}
结果如下
stencil的值在球体绘制的地方被赋为2,没有绘制的地方为0.
接下来画绿色的球
Shader "Custom/GreenStencil" {
SubShader {
Tags { "RenderType"="Opaque" "Queue"="Geometry+1"}
Pass {
Stencil {
Ref 2
Comp equal
Pass keep
Fail decrWrap
ZFail IncrSat
}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
struct appdata {
float4 vertex : POSITION;
};
struct v2f {
float4 pos : SV_POSITION;
};
v2f vert(appdata v) {
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
return o;
}
half4 frag(v2f i) : SV_Target {
return half4(0,1,0,1);
}
ENDCG
}
}
FallBack "Diffuse"
}
绘制结果如下,绿色的部分是stencil pass的,绿球没有被绘制出来的地方stecil值会减一,变为255.有一部分通过的stencil test,但是没有通过ztest的,stencil值+1,变成3
最后画蓝色的球,shader如下
Shader "Custom/BlueStencil" {
SubShader {
Tags { "RenderType"="Opaque" "Queue"="Overlay"}
Pass {
Stencil {
Ref 3
Comp Equal
}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
struct appdata {
float4 vertex : POSITION;
};
struct v2f {
float4 pos : SV_POSITION;
};
v2f vert(appdata v) {
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
return o;
}
half4 frag(v2f i) : SV_Target {
return half4(0,0,1,1);
}
ENDCG
}
}
}
蓝色球只在stencil buffer 值为3的地方绘制出来了。
参考
ShaderLab: Stencil - http://docs.unity3d.com/Manual/SL-Stencil.html
Extra buffers - https://open.gl/depthstencils
OpenGL Programming/Stencil buffer - https://en.wikibooks.org/wiki/OpenGL_Programming/Stencil_buffer