首页 > 其他分享 >Mask实现原理(兼模板测试小结)

Mask实现原理(兼模板测试小结)

时间:2024-04-12 21:33:05浏览次数:25  
标签:ref Mask 像素 测试 缓冲区 小结 模板 stencilBufferValue

前言

虽然说网上已经有不少优秀的总结,但为了让知识停留在脑海里,我还是决定自己总结一份笔记。


大概思路

  1. Mask会修改Graph组件的材质为StencilMaterial,该材质的作用是给每个不透明的像素标记,将标记结果存入模板缓冲区中。
  2. 当子级UI进行模板测试时,如果通过就渲染,没通过就不渲染。

模板测试是怎么一回事

既然遮罩跟模板测试有关,我们就要了解下模板测试是怎么一回事。
模板缓冲区跟颜色缓冲区和深度缓冲区类似,模板缓冲区可以为“屏幕”上每个像素保存一个无符号整数(通常是8位)。在渲染的过程中,可以用一个程序设定的参考值和这个值进行比较,根据比较结果来决定是否更新相应像素的颜色值,这个比较过程就算模板测试。
image
从图中可以看到,模板测试通常发生在alpha测试之后,深度测试之前。模板测试可以指定应用场景,通常应用于Cull Back的几何体。除非Cull Front被指定,在这种情况下应用于正面消隐藏的几何体。当然也可以双面都指定。

模板测试语法
stencil
{
	Ref referenceValue
	ReadMask  readMask
	WriteMask writeMask
	Comp comparisonFunction
	Pass stencilOperation
	Fail stencilOperation
	ZFail stencilOperation
}
Ref

用来设定参考值,这个值用来与模板缓冲区中的值作比较,取值范围0-255。

ReadMask

ref值和stencilBufferValue值做比较前,ReadMask会分别和它们做次按位与(&)运算,ReadMask取值范围0-255,默认为11111111。

WriteMask

对写入模板缓冲区的值做次按位与(&)运算,WriteMask取值范围0-255,默认为11111111。

Comp

定义 参考值(RefValue)和缓冲值(stencilBufferValue) 比较的操作函数,默认:always。

Greater 相当于“>”操作,即仅当左边>右边,模板测试通过,渲染像素
GEqual 相当于“>=”操作,即仅当左边>=右边,模板测试通过,渲染像素
Less 相当于“<”操作,即仅当左边<右边,模板测试通过,渲染像素
LEqual 相当于“<=”操作,即仅当左边<=右边,模板测试通过,渲染像素
Equal 相当于“=”操作,即仅当左边=右边,模板测试通过,渲染像素
NotEqual 相当于“!=”操作,即仅当左边!=右边,模板测试通过,渲染像素
Always 不管公式两边为何值,模板测试总是通过,渲染像素
Never 不管公式两边为何值,模板测试总是失败 ,像素被抛弃
Pass

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

Fail

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

ZFail

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

Keep 保留当前缓冲中的内容,即stencilBufferValue不变
Zero 将0写入缓冲,即stencilBufferValue值变为0
Replace 将参考值写入缓冲,即将ref值赋给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
模板测试公式
if(referenceValue & readMask comparisonFunction stencilBufferValue & readMask)
{
	通过像素
}
else
{
	抛弃像素
}
例子
// 第一个Pass
ColorMask 0			// 不输出颜色到屏幕
ZWrite Off			// 关闭深度写入,避免将后面的像素剔除
stencil{
	Ref 1			// 将ref值定义为1
	Comp always		// 不管stencilBufferValue值为多少,模板测试都通过
	Pass replace		// 将参考值2写入模板缓冲区当中
}

// 第二个Pass

Stencil {
      Ref 1			// 将ref值定义为1
      Comp Equal		// ref值需要和stencilBufferValue值相同,才能通过测试,渲染到屏幕上
}

image
屏幕中所有像素,绿色方框内的模板缓冲区值都是2,后面人物模型用第二个Pass的Shader,只有模板缓冲区为1的像素点才能渲染到屏幕上。

Mask是怎么做到遮挡的

了解完模板测试原理,我们来看下Unity内Mask是怎么做到遮挡的,在场景中添加2个Image,Image-1是Image-2的父节点,其中Image-1添加Mask组件。
image
我们可以看到给Image-1添加了Mask组件,同时修改了Image-1和Image-2的材质为。
image
Image1开启了模板测试,Op为Always,Comp为Replace,意思是不管怎样都将1写入到模板缓冲区当中。
Image2同样开启了模板测试,但它没有开启写入,Op为Keep,保持原状;Comp为Equeal,WriteMask为1,ref值为1,意思是仅有模板缓冲区当中值为1的像素点才能被显示在屏幕上。

代码添加

Mask.GetModifiedMaterial

        /// Stencil calculation time!
        public virtual Material GetModifiedMaterial(Material baseMaterial)
        {
            if (!MaskEnabled())
                return baseMaterial;

            var rootSortCanvas = MaskUtilities.FindRootSortOverrideCanvas(transform);
            var stencilDepth = MaskUtilities.GetStencilDepth(transform, rootSortCanvas);
            if (stencilDepth >= 8)
            {
                Debug.LogWarning("Attempting to use a stencil mask with depth > 8", gameObject);
                return baseMaterial;
            }

            int desiredStencilBit = 1 << stencilDepth;

            // if we are at the first level...
            // we want to destroy what is there
            if (desiredStencilBit == 1)
            {
                var maskMaterial = StencilMaterial.Add(baseMaterial, 1, StencilOp.Replace, CompareFunction.Always, m_ShowMaskGraphic ? ColorWriteMask.All : 0);
                StencilMaterial.Remove(m_MaskMaterial);
                m_MaskMaterial = maskMaterial;

                var unmaskMaterial = StencilMaterial.Add(baseMaterial, 1, StencilOp.Zero, CompareFunction.Always, 0);
                StencilMaterial.Remove(m_UnmaskMaterial);
                m_UnmaskMaterial = unmaskMaterial;
                graphic.canvasRenderer.popMaterialCount = 1;
                graphic.canvasRenderer.SetPopMaterial(m_UnmaskMaterial, 0);

                return m_MaskMaterial;
            }

            //otherwise we need to be a bit smarter and set some read / write masks
            var maskMaterial2 = StencilMaterial.Add(baseMaterial, desiredStencilBit | (desiredStencilBit - 1), StencilOp.Replace, CompareFunction.Equal, m_ShowMaskGraphic ? ColorWriteMask.All : 0, desiredStencilBit - 1, desiredStencilBit | (desiredStencilBit - 1));
            StencilMaterial.Remove(m_MaskMaterial);
            m_MaskMaterial = maskMaterial2;

            graphic.canvasRenderer.hasPopInstruction = true;
            var unmaskMaterial2 = StencilMaterial.Add(baseMaterial, desiredStencilBit - 1, StencilOp.Replace, CompareFunction.Equal, 0, desiredStencilBit - 1, desiredStencilBit | (desiredStencilBit - 1));
            StencilMaterial.Remove(m_UnmaskMaterial);
            m_UnmaskMaterial = unmaskMaterial2;
            graphic.canvasRenderer.popMaterialCount = 1;
            graphic.canvasRenderer.SetPopMaterial(m_UnmaskMaterial, 0);

            return m_MaskMaterial;
        }

其中

参考资料

https://blog.csdn.net/u011047171/article/details/46928463
https://blog.csdn.net/u013477973/article/details/89343777
https://www.cnblogs.com/j349900963/p/8340571.html

标签:ref,Mask,像素,测试,缓冲区,小结,模板,stencilBufferValue
From: https://www.cnblogs.com/chenxiayun/p/18129989

相关文章

  • 【模板】数学
    高精度类structbint:Vi{ bint(intn=1){resize(n);} voidclr(){while(size()>1&&!back())pop_back();} friendistream&operator>>(istream&io,bint&a){ strings;io>>s; a.clear();rFor(i,sz(s)-1,0)a.pb......
  • Thymeleaf SSTI模板注入分析
    环境搭建先搭建一个SpringMVC项目,参考这篇文章,或者参考我以前的spring内存马分析那篇文章https://blog.csdn.net/weixin_65287123/article/details/136648903SpringMVC路由简单写个servletpackagecom.example.controller;importorg.springframework.stereotype.Controlle......
  • 模板函数使用类型推导时的bug
    templatestaticboolparse_a_value(T&val,Json::Valuejson_val){if(json_val.isNull())returnfalse;if(typeid(val)==typeid(int)||typeid(val)==typeid(int16_t)||typeid(val)==typeid(int8_t)||typeid(val)==typeid(int32_t)){......
  • SOLIDWORKS模板批量修改工具 慧德敏学
    SOLIDWORKS批量修改模板插件-SolidKits.BOMs工具可实现工程图模板的批量替换,单位系统的批量修改,批量定义模型材质等功能。操作简单快捷,只需要提前打开SOLIDWORKS软件,执行后程序会自动完成所有替换操作。使用SOLIDWORKS绘制工程图之前,必须要选择工程图模板,模板中我们会定义好图幅......
  • windows下配置mask2former(facebook版)
    由于此版本的mask2former官方只提供了macOS和Linux的安装说明,所以windows安装会趟一些坑记录一下1.安装Anaconda2.安装PyCharm3.创建python3.8环境(最高3.8因为有一个依赖包最高支持python3.8)4.安装GCC下载地址:https://sourceforge.net/projects/mingw/点击Download下载......
  • 软件开发文档模板全套合集(开发+实施+运维+安全+交付)
    前言:在软件项目管理中,每个阶段都有其特定的目标和活动,确保项目的顺利进行和最终的成功交付。以下是软件项目管理各个阶段的详细资料:软件项目全套文档资料下载:点我获取1.需求阶段目标:收集、分析和定义用户需求和业务目标。主要活动:需求调研:与用户沟通,了解他们的需求和期......
  • 实用算法模板——滑动窗口
    为了更好的说明这个问题,我们借用acWing上的一道题目模拟样例:解法一:使用stl中的双端队列求解解法二:使用数组模拟队列,运行速度更快如果还有疑问,可参考:C15【模板】单调队列滑动窗口最值_哔哩哔哩_bilibili希望对你有所帮助,感谢查看!......
  • 算法模板 v1.12.2.20240411
    算法模板v1.1.1.20240115:之前历史版本已不可寻,创建第一份算法模板。v1.2.1.20240116:删除“编译”-“手动开栈”;删除“编译”-“手动开O优化”;修改“编译”-“CF模板”;删除“读写”;删除“图论”-“欧拉图”-“混合图”;删除“图论”-“可达性统计”;删除“数据类型”-“高精类”。......
  • C++——模板初阶
    目录0.前言1.泛型编程2.函数模板2.1函数模板概念2.1函数模板格式2.3函数模板的原理2.4函数模板的实例化2.5模板参数的匹配原则3.类模板3.1类模板的定义格式3.2类模板的实例化0.前言C++模板是一种泛型编程的工具,允许开发者定义对多种数据类型都适用的代......
  • 【模板】任意模数多项式乘法:三模 NTT
    前置知识https://www.cnblogs.com/caijianhong/p/template-crt.htmlhttps://www.cnblogs.com/caijianhong/p/template-fft.html题目描述任意模数多项式乘法solution首先我们打开https://blog.miskcoo.com/2014/07/fft-prime-table这篇文章找到\(998244353\)附近的几个质......