(九)Mask详解
1.前言
本文从逻辑和原理上详细分析Ugui的Mask组件。Mask组件的逻辑没有RectMask2D复杂,但是原理稍微麻烦一点,用得到渲染的模板检测。
2.模板遮罩原理
2.1 模板值
以下图为例,假如均没有开启模板检测,canvas上只有一个RawImage组件,那么整个背景模板值为0。如果图中RawImage开启模板检测,并设置模板检测值为2(模板检测参考值是自定义的)。那么图片区域模板值为2,以外红色区域为0(模板值是以像素为单位进行设置的),如果开启根据透明通道值设置(Use Alpha Clip为true),则RawImage非透明区域为2,其他区域为0。
2.2 模板参数设置
模板值通过shader中设置相关变量实现的,我们通过ui默认的shader来解释。新建Material,并设置shader为UI/default,并赋值给一个RawImage,则可以看到参数面板如下,则可进行相关参数设置:
我们来解释一下参数及其意义:
Stencil ID:设置的模板参考值,如图设置为2
Stencil Operation:模板值操作方式,即如何进行操作,如果如图设置为2。则表示replace,即用Stencil ID的值代替此ui覆盖区域的像素的模板值。参考值如下所示:
namespace UnityEngine.Rendering
{
public enum StencilOp
{
Keep = 0,//保持原值
Zero = 1,//设置为0
Replace = 2,//用当本参考值代替原值
IncrementSaturate = 3,//逐次增加,当超过255时保持255
DecrementSaturate = 4,//逐次减小,当少于0时保持0
Invert = 5,//按位取反
IncrementWrap = 6,//逐次增加,当超过255时继续从0开始
DecrementWrap = 7//逐次减小,当少于0时继续从255开始
}
}
Stencil Comparison:比较方法,参考值如下,如果设置为3(即equal),则表示如果当前RawImage渲染时,模板值如果与之前模板值相同(即通过模板测试)则渲染当前RawImage像素,反之舍弃。
namespace UnityEngine.Rendering
{
public enum CompareFunction
{
Disabled = 0,//不进行深度和模板检测
Never = 1,//模板和深度检测均不通过
Less = 2,//小于前模板值时通过检测
Equal = 3,//等于前模板值时通过检测
LessEqual = 4,//小于等于前模板值时通过检测
Greater = 5,//大于前模板值时通过检测
NotEqual = 6,//不等于前模板值时通过检测
GreaterEqual = 7,//大于等于前模板值时通过检测
Always = 8//一直通过检测,与1相反
}
}
Stencil Read/Write Mask:即读取或者重新写入模板值时的Mask,即与填写的值按位与进行计算。如果255则表示值不变,即取出/写入多少就是多少。
Color Mask:表示写入颜色值时写入的成分。如下所示:
namespace UnityEngine.Rendering
{
[Flags]
public enum ColorWriteMask
{
Alpha = 1,
Blue = 2,
Green = 4,
Red = 8,
All = 15
}
}
Use Alpha Clip:是否进行根据通道值剔除,如果开启则会按像素进行处理,否则按Rect形状进行模板值操作。
2.3 模板参数使用
模板测试分两步进行,即模板值测试与模板值设置。
2.3.1 模板测试
用StecilBufferValue来代替模板缓冲值,则如果满足(Stencil ID&Read Mask) StencilComparison (StencilBufferValue&Read Mask)则表示通过测试,即当前像素可以写入FrameBuffer(若不理解可认为是写到屏幕上)。否则抛弃像素。
2.3.2 模板设置
如果Stencil Operation值为Replace,则表示设置当前像素位置的模板值为Stencil ID,如果为IncrementSaturate,则表示再当前卖报纸的基础上加1,如果超过255,则保持255。模板测试之后,无论模板测试通过与否,都要对模板进行相应的更新
2.4 模板测试应用
假如在上一张图片的基础上再加一张图片,且这两张均未开启模板检测,则显示结果如下。因为渲染顺序问题,则蓝色路飞头像会遮挡前面山水图。
然后设置山水图模板参数如下所示:
如上图片所示,我们开启模板检测,并设置山水图模板值为2。StencilComparision值为8,表示直接通过模板测试。则将此图片像素信息全部写入缓冲区(因为ColoMask值为15)。由于StencilOperation值为2(替换),则将StencilID值写入覆盖区域。对第二张蓝色路飞图开启模板检测,设置模板参数如下图所示。由于其StencilComparision参数为3,则表示若前后模板值相等即均为2,则通过测试保留像素值,否则丢弃此像素值。同时保持模板值不变,即原来是2的区域还是2,是0的区域还是0。
效果如如下所示:
如果将第二张图片StencilComparision为5,即如果大于模板值则保留像素,那么第一章图片区域的像素就会被舍弃,而其他区域会被保留,效果类似于挖洞,如下所示:
3.Mask逻辑
上述为Mask的实现原理,至于子物体全部遮罩则是逻辑设置参数实现的。流程如下:
1)Mask启动时通知所有子游戏物体,可以进行进行模板值重新计算(m_ShouldRecalculateStencil标记为true)。
2)Graphic进行渲染时会设置材质,会调用如下属性,会查找所有IMaterialModifier,修改模板值。
public virtual Material materialForRendering
{
get
{
var components = ListPool<Component>.Get();
GetComponents(typeof(IMaterialModifier), components);
var currentMat = material;
for (var i = 0; i < components.Count; i++)
currentMat = (components[i] as IMaterialModifier).GetModifiedMaterial(currentMat);
ListPool<Component>.Release(components);
return currentMat;
}
}
3)由于Mask和MaskableGraphic均继承实现接口IMaterialModifier,所以根据层级设置相关模板参数,从而实现子物体遮罩想过。
4.结语
以上为Mask的基本原理与基本逻辑