Stencil在Unity中的使用

(一)、Stencil的理论知识

Stencil用于在渲染时保留和抛弃像素

Stencil对应的材质组件在Unity中的显示如图:
Stencil在Unity中的使用_第1张图片

1. Ref ReferenceValue

对应组件中的Stencil ID由程序自己设定,用于和缓冲(Stencil Buffer)中的值相比较,比较的方式在Stencil Comparison中设定,满足条件则渲染出来(保留)。

2. Stencil Comparison

渲染时判断是否保留的条件。将ref中的value与buf中的value进行比较公式如下:

if((refValue & readMask) comparisonFunction (bufferValue & readMask)) 
{ 通过测试,渲染时保留 }
else 
{ 渲染时抛弃 }

具体的方式包括:

比较方法 含义
Disabled 0 不进行Stencil与深度测试
Never 1 无论左边和右边为何值,使测试永远不通过,渲染时总是抛弃
Less 2 左边 < 右边时通过测试,渲染时保留,否则抛弃
Equal 3 左边 = 右边时通过测试,渲染时保留,否则抛弃
LessEqual 4 左边 <= 右边时通过测试,渲染时保留,否则抛弃
Greater 5 左边 > 右边时通过测试,渲染时保留,否则抛弃
NotEqual 6 左边 != 右边时通过测试,渲染时保留,否则抛弃
GreaterEqual 7 左边 >= 右边时通过测试,渲染时保留,否则抛弃
Always 8 使测试永远通过,渲染时总是保留(是Stencil Comparison的默认值即8)

具体对应的值可在Unity的接口UnityEngine.Rendering.CompareFunction枚举中查看,当所填值不在该范围内时采用默认值8即Always渲染时总是保留。

3. Stencil Operation

渲染测试之后,无论像素最终被保留或被抛弃都会执行Stencil Operation来更新缓冲中的值stencilBufferValue,具体更新的方式包括:

更新方式 含义
Keep 0 保持当前buf的值不变
Zero 1 不管ref值为何值都将当前buf的值置0
Replace 2 将ref值写入buf,即将原来的buf值替换为现在的ref值
IncrementSaturate 3 不管ref值为何值都将当前buf的值+1,超过255不变
DecrementSaturate 4 不管ref值为何值都将当前buf的值置-1,超过0不变
Invert 5 不管ref值为何值都将当前buf的值按位取反
IncrementWrap 6 不管ref值为何值都将当前buf的值+1,若当前值为255增1则变为0
DecrementWrap 7 不管ref值为何值都将当前buf的值-1,若当前值为0减1则变为255

具体对应的值可在Unity的接口UnityEngine.Rendering.StencilOp枚举中查看,当所填值不在范围内时采用默认值zero(试验得来·)。

4. Read Mask & Write Mask

读遮罩与写遮罩,分别在读取与写入时对ref的值与buf的值进行掩码操作,最大255,默认255即不对ref值与buf值进行操作,直接读或者写。

(二)、Stencil实例

1. 新建材质

Stencil在Unity中的使用_第2张图片
新建材质MatCube、MatSphere,shader选择带Stencil的即可,可见Stencil Comparison的默认值是8即总在渲染时保留,另外将MatCube颜色设成黑色、MatSphere颜色设成白色便于区分。

2. 新建Cube与Sphere

新建Cube与Sphere,并将Cube放大2倍,调整好相对位置,设置好相机便于观察,Scene与Game视图如图,此时Cube与Sphere都被渲染出来了。
Stencil在Unity中的使用_第3张图片

3. 设置参数

Stencil在Unity中的使用_第4张图片
这里参数的含义是将方块永远渲染出来并且将buf中的值(默认是0)替换成2,这是界面不会有任何变化,因为球体的Stencil Comparison是8默认渲染时全部保留,不管ref与buf中的值是多少。如图所示:
Stencil在Unity中的使用_第5张图片
再将球体的Stencil Comparison改为3,即等于0的部分渲染时保留,此时界面就变成了这样,如图,因为方块已经将方块区域buf的值改成了2,而整个小球所有的ref值都是0,所以只有没被方块改变的部分,渲染时保留了下来。
Stencil在Unity中的使用_第6张图片
再将球体的ref值改为2,结果如图,只有buf值等于2的部分被渲染出来了。
Stencil在Unity中的使用_第7张图片

4. 总结与注意

buf中值的更改总是作用于被Stencil影响的区域,没有影响的区域buf的默认值是0,并且buf中值的更新总是在渲染测试之后,所以在只有方块和球体且渲染顺序是Cube > Sphere的情况下,MatSphere中Stencil Operation无论为何值都不会影响方块与球体的渲染,只会影响后续在此位置的物体的渲染。

(三)、利用代码改变Stencil的相关参数

private Material m_MatSphere;
	void Start () 
	{
        //int op = (int)UnityEngine.Rendering.StencilOp.Replace;
        //int com = (int)UnityEngine.Rendering.CompareFunction.Equal;
        m_MatSphere = transform.GetComponent<Renderer>().material;
        if (m_MatSphere.HasProperty("_StencilComp") && m_MatSphere.HasProperty("_Stencil") && m_MatSphere.HasProperty("_StencilOp"))
        {
            //float value = m_MatSphere.GetFloat("_StencilComp");
            m_MatSphere.SetFloat("_StencilComp", 3);
            m_MatSphere.SetFloat("_Stencil", 3);
            m_MatSphere.SetFloat("_StencilOp", 3);
        }
    }

具体这些key可以先选中材质然后Edit Shader看到,如图所示:
Stencil在Unity中的使用_第8张图片
将脚本挂在Sphere上,运行游戏,结果如下:
Stencil在Unity中的使用_第9张图片

(四)、渲染顺序

关于物体的渲染顺序可以在Frame Debugger中查看Windows -> Analysis -> Frame Debugger。如图:
Stencil在Unity中的使用_第10张图片
点击Enable即可得到当前场景的渲染信息,点击小箭头也可查看每一步的渲染信息,如图也能看到当前场景中Cube的渲染是优于Sphere的所以Cube的Stencil Operation是能够影响Sphere的渲染的。
Stencil在Unity中的使用_第11张图片

注意:当Stencil与Mask(比如使用ScrollRect时)一块使用时,会出现混乱,可以取消使用Mask,在ScrollRect与其子物体上使用Stencil来控制显示范围

你可能感兴趣的:(Unity)