沿着 Unity Image 面板逐项拆解 UI:从贴图、顶点色与材质,到 Mask 的模板测试及不同 Image Type 的网格变化。
最近学习到了很多Unity自带的UI功能,发现之前shader做的某些东西并不需要shader来实现,UI本身就可以实现一部分。了解这些功能的本质有助于之后写shader实现更复杂的功能的时候对这些功能的适配。
首先是UI本身的本质:
其实无论是UI还是场景中的2D,它的本质就是一个3D的面片,一般是由两个三角形组成的矩形,只不过我们常规的3D模型是由我们自己指定模型,而UI和2D是unity帮我们创建的面片而已。
理解了它的话,我们就知道,其实UI并没有什么特殊的,3D能做的效果,处理对UI一样使用,想要拿到的各种数据也都可以拿到的。
剩下的内容,就沿着Image面板逐个讲起吧。
Source Image
内容很简单,就是输入贴图,对应shader中的_MainTex,想怎么用都行,不过Unity中将材质球的_MainTex面板锁定住了,只能通过Source Image来输入想要的贴图。
Color
这个Color对应的模型顶点色,所以属于模型本身的属性,所以不会打断合批,当然如果不需要的话,拿来装一些自定义的数据也是可以的。
Material
材质,如果不填入的话就会自动使用Unity的一个UI基础材质,如下
Raycast Target/Raycast Padding
与检测鼠标点击的射线检测有关,跟渲染关系不大,就不介绍了
Maskable
控制组件是否受到父级的Mask影响。 Mask本质是由模板测试来实现的,模板测试相关内容可以查阅ShaderLab 命令:模板 - Unity 手册。
模板测试
在渲染流水线中,除了屏幕中用于显示物体颜色的颜色缓冲,还有深度缓冲和模板缓冲。通常背景的模板缓冲值为0,在某个像素被渲染的时候,可以根据模板测试的设置,来将某个模板值写入模板缓冲区,后续物体的模板值会依据当前物体的模板测试设置来进行比较,例如UI的Mask功能,子物体被设置为,如果当前像素与模板缓冲的值相同,就绘制这个像素。
Mask功能的模板测试
通常物体在渲染时与背景相同,模板测试的值为0,这样整个画面就会保持一个模板值为0的状态
当Mask功能开启的时候,会经历以下流程:父物体将模板值写入模板缓冲→子物体依据模板测试,只在与父物体模板值相同的位置绘制→再绘制一次父物体,将其模板值归零,防止污染后续模板缓冲。所以我们可以看到明明只有两个物体,却调用了3次绘制:
下面将一次梳理下这三次绘制: 第一次绘制对象是父物体
我们依次分析其中重要的部分: - Stencil Ref 1 :模板测试的参考值是1 - Stencil Comp Always :通过条件是永远通过模板测试,也就是说父物体的像素永远不会因为模板测试而被取消绘制 - Stencil Pass Replace:当通过模板测试的时候,将参考值写入模板缓冲中,也就是之前说过的“1”,现在只要是这个金币所在的区域,所有模板值都为1了。 第二次绘制对象是子物体
- Stencil Ref 1 :模板测试的参考值依旧是1
- Stencil ReadMask 1 :当读取缓冲中的参考值时,只读取二进制的最后一位,也就是他只和上图括号中的最后一位比较
- Stencil Comp Equal :只有当前像素与模板缓冲区里值相同时才会通过,前文提到,硬币的区域是“1”,背景是“0”,所以子物体只会在硬币的区域被绘制。
- Stencil Pass Keep :当模板测试通过时保持模板缓冲区不变 第三次绘制还是父物体:
第三次的功能是清除覆盖的模板缓冲区,防止污染之后的模板测试,所以他使用的还是父物体绘制,我们可以看到最下面_MainTex的位置还是黄色的硬币,为了防止这次绘制覆盖子物体,它将ColorMask设置为0,也就是说无论这次各种测试结果怎样都不会讲像素绘制到屏幕上。 然后来看模板测试设置:
我们只需要看两个位置: - Stencil Comp Always :这次绘制依旧永远通过模板测试, - Stencil Pass Zero :当模板测试通过时将模板缓冲区归零,也就避免了模板测试污染。 正因为每次都会将模板值归零,所以无论使用多少个平行的父物体,都可以从1开始设置模板测试。
Maskable开关
知道了这些内容我们就可以去看看Maskable的开关到底做了什么,它做的事情非常简单:模板测试永远通过,模板缓冲值不变,就只有这两件事,我们直接看模板设置:
其实只要能看懂模板测试设置,这些问题都迎刃而解了。 对于自定义shader应该注意适配他的模板测试功能。
ImageType
它用来设置各种UI的渲染模式,通过它们其实也可以达到一些蛮丰富的UI功能了。
Simple
这个模式是最常规的UI模式,普通UI都使用这个模式。 Use Sprite Mesh会让UI的顶点以更贴合贴图本身的形式分布,而不是一个矩形。
Preserve Aspect可以让UI永远保持原先的比例
不过它是靠防止网格拉伸来实现的,所以这种模式不能带来额外的绘制面积。
Sliced
这个功能可以保证UI边角的一致性,从而适配任意大小,他的它质其实是按照拉伸SpriteEditor的设置拉伸整个模型:
正因如此,整个模型的UV比例也会随着模型拉伸而产生变化,也就不可能去再用这个UV来采样规则模型了。
Tlied
这个功能常常用于需要重复平铺的背景,当贴图的WrapMode设置为Repeat时,可以做到下面这样的效果:
其本质是对于模型拉伸时UV的处理方式:
对于超出的部分,普通的Simple是将这部分拉伸为0-1的值,对应到UV的贴图采样中,就是贴图的拉伸和放大,而Tlied模式则是将超出的部分作为2,3,4,5….来处理,对于Repeat来说每个数字区间的值就是0-1的值,所以相当于将很多个小矩形平铺成为了一个巨大的矩形。
Filled
Filled可以做到这样的效果:
实际上也是对模型本身动的手: