遮罩,顧名思義是一種可以掩蓋其它元素的控制元件,常用于修改其它元素的外觀,或限制元素的形狀,比如ScrollView或者圓頭像效果都有用到遮罩功能,本系列文章希望通過閱讀UGUI原始碼的方式,來探究遮罩的實作原理,以及通過Unity不同遮罩之間實作方式的對比,找到每一種遮罩的最佳使用場合,
本文是UGUI遮罩系列的第三篇,也是最后一篇,前兩篇分別是對Mask和RectMask2D的原始碼分析,詳細解讀了它們的原理與實作細節,這次的側重點是對Mask和RectMask2D做一個對比分析,同時總結一下在Mask和RectMask2D不起作用的場景下如何實作遮罩效果,本文大部分內容建立在讀者已了解Mask與RectMask2D原理的基礎之上,所以在閱讀本文前建議先看下前兩篇文章,
- 【UGUI原始碼分析】Unity遮罩之Mask詳細解讀
- 【UGUI原始碼分析】Unity遮罩之RectMask2D詳細解讀
本文所做的一些測驗與驗證均基于Unity2019.4版本
Mask與RectMask2D對比
1. Mask遮罩的大小與形狀依賴于Graphic,而RectMask2D只需要依賴RectTransform
Mask是利用Graphic渲染時修改對應片元的模板值來確定遮罩的大小與形狀的,Graphic的形狀決定了Mask遮罩的形狀,因此缺少Graphic組件,Mask遮罩將會失效,當禁用了物件的Graphic組件,比如Image組件,Unity會有以下警告提示

RectMask2D是利用自己的RectTransform計算出裁剪矩形,然后降低不在矩形內的片元透明度來實作遮罩效果,因此不需要依賴Graphic組件
2. Mask支持圓形或其他形狀遮罩, 而RectMask2D只支持矩形
Mask遮罩形狀可以更加多樣,由于Mask遮罩的形狀由Graphic決定,所以利用不同的Graphic可以實作不同形狀的遮罩
而RectMask2D通過RectTransform計算裁剪矩形的機制導致它只能支持矩形遮罩,僅在 2D 空間中有效,不能正確掩蓋不共面的元素
3. Mask會增加drawcall
除了繪制元素本身所需的1個drawcall以外,Mask還會額外增加2個drawcall,一個用來在繪制元素前修改模板緩沖的值,另一個用來在所有UI繪制完后將模板緩沖的值恢復原樣
舉個栗子,如下所示的一個UI場景,畫布下一個帶有Mask組件的panel父節點,其下有一個子節點Image

通過Unity的幀除錯器查看渲染程序,共有3次drawcall

3次drawcall的區別主要在于模板引數的不同,第一次是總是通過(Stencil Comp:Always)模板測驗,并將模板值替換(Stencil Pass:Replace)為1(Stencil Ref:1),第二次是用于繪制Image的,第三次是總是通過(Stencil Comp:Always)模板測驗,并將模板值設定為0(Stencil Pass:Zero),即起到擦除模板值的作用,

4. RectMask2D可能會破壞合批
有如下所示的一個測驗場景,Panel1和Panel2都是只掛有RectTransform組件的單純父節點,其下都有一個Image子節點,正常情況下應該可以合批,drawcall應為1

通過幀除錯器查看,確實如此,成功合批,drawcall是1

此時給Panel1添加一個RectMask2D組件,實作遮罩效果,Panel2保持不變

通過幀除錯器查看,drawcall數量是2,原本的合批被破壞了,

由此,網上查到的一些資料會得出“RectMask2D節點下的所有孩子都不能與外界UI節點合批且多個RectMask2D之間不能合批”的結論,實際上這是一種不嚴謹的說法,甚至是錯誤的,要搞清楚這個問題,需要先弄明白為什么RectMask2D會破壞合批?
通過幀除錯器可以發現,是RectMask2D傳遞裁剪矩形時,修改了Shader的引數,導致不能合批,從下圖可以看到2次drawcall的區別就在于_ClipRect不同


既然是裁剪矩形引數不同導致不能合批,那如果將兩個裁剪矩形引數設定為一致是不是就能合批了呢?動手驗證一下,給Panel1和Panel2都添加上RectMask2D組件,同時將它們的RectTransform引數設定為完全一致(這樣可以保證裁剪矩形引數相同),然后把Panel1的子節點Image往左移,Panel2的子節點Image往右移,讓它們都能顯示出來,最終效果如下圖所示

再次測驗后可以看到drawcall只有1次了,_ClipRect是相同的值,因此可以得出結論,RectMask2D確實由于裁剪矩形引數的設定會破壞合批,但不是一定的,在滿足條件時,RectMask2D節點下的孩子也能與外界UI節點合批,多個RectMask2D之間也是能合批的,

5. Mask與RectMask2D用哪個?
Mask的實作利用了模板緩沖區,會增加2個drawcall,性能會受到一定影響,簡單的UGUI界面,還是建議使用RectMask2D,相對來說性能更強,也無需額外的繪制呼叫,但由于RectMask2D也有可能破壞合批,在復雜的情況下,并沒有確切的結論來判斷哪個更優,只能利用工具實際測驗找到最優者,具體問題具體分析才是正確做法,當然,諸如圓形遮罩等一些RectMask2D無法勝任的場景,還是要使用Mask
粒子系統實作遮罩效果
游戲的UI界面也經常會添加粒子效果,有時也會需要對粒子添加遮罩,Mask和RectMask2D只適用于UGUI,對粒子系統無法生效,此時可以使用SpriteMask,SpriteMask的原理與Mask相同,都是基于模板測驗實作,

粒子系統的Renderer模塊有對應的Mask屬性設定,可以調整粒子在精靈遮罩外部和內部的可見性

MeshRenderer實作遮罩效果
UI界面添加的一些特效也有可能是MeshRenderer實作的,例如利用Shader制作的頂點影片,但MeshRenderer沒有提供Mask相關設定,無法使用遮罩,好在基于模板測驗實作遮罩的原理都是相同的,可以自己動手修改MeshRenderer使用的材質,在Shader中添加ShaderLab模板配置來使用模板測驗
需要添加到Shader中的代碼如下所示
Properties
{
_StencilComp ("Stencil Comparison", Float) = 8
_Stencil ("Stencil ID", Float) = 0
_StencilOp ("Stencil Operation", Float) = 0
_StencilWriteMask ("Stencil Write Mask", Float) = 255
_StencilReadMask ("Stencil Read Mask", Float) = 255
}
Stencil
{
Ref [_Stencil]
Comp [_StencilComp]
Pass [_StencilOp]
ReadMask [_StencilReadMask]
WriteMask [_StencilWriteMask]
}
添加完成后,材質界面會多出模板相關的配置,如下所示

再配合SpriteMask,修改對應的模板引數,就可以模擬遮罩效果了,例如
- Stencil Comparison設定為3,就相當于"Visible Inside Mask"
- Stencil Comparison設定為6,就相當于"Visible Outside Mask"
- Stencil Comparison設定為8,就相當于"No Masking"
參考
- 【Unity原始碼學習】遮罩:Mask與Mask2D
- 原始碼探析Mask、Rect Mask2D與Sprite Mask的異同
- UI上的特效的裁剪問題
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/296253.html
標籤:其他
上一篇:【UGUI原始碼分析】Unity遮罩之RectMask2D詳細解讀
下一篇:從零開始制作一個粒子系統
