天天看點

Unity遮罩之Mask、RectMask2D與Sprite Mask适用場景分析

遮罩,顧名思義是一種可以掩蓋其它元素的控件。常用于修改其它元素的外觀,或限制元素的形狀。比如ScrollView或者圓頭像效果都有用到遮罩功能。本系列文章希望通過閱讀UGUI源碼的方式,來探究遮罩的實作原理,以及通過Unity不同遮罩之間實作方式的對比,找到每一種遮罩的最佳使用場合。

本文是UGUI遮罩系列的第三篇,也是最後一篇。前兩篇分别是對Mask和RectMask2D的源碼分析,詳細解讀了它們的原理與實作細節。這次的側重點是對Mask和RectMask2D做一個對比分析,同時總結一下在Mask和RectMask2D不起作用的場景下如何實作遮罩效果。本文大部分内容建立在讀者已了解Mask與RectMask2D原理的基礎之上,是以在閱讀本文前建議先看下前兩篇文章。

  1. 【UGUI源碼分析】Unity遮罩之Mask詳細解讀
  2. 【UGUI源碼分析】Unity遮罩之RectMask2D詳細解讀

本文所做的一些測試與驗證均基于Unity2019.4版本

Mask與RectMask2D對比

1. Mask遮罩的大小與形狀依賴于Graphic,而RectMask2D隻需要依賴RectTransform

Mask是利用Graphic渲染時修改對應片元的模闆值來确定遮罩的大小與形狀的,Graphic的形狀決定了Mask遮罩的形狀。是以缺少Graphic元件,Mask遮罩将會失效。當禁用了對象的Graphic元件,比如Image元件,Unity會有以下警告提示

Unity遮罩之Mask、RectMask2D與Sprite Mask适用場景分析

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遮罩之Mask、RectMask2D與Sprite Mask适用場景分析

通過Unity的幀調試器檢視渲染過程,共有3次drawcall

Unity遮罩之Mask、RectMask2D與Sprite Mask适用場景分析

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

Unity遮罩之Mask、RectMask2D與Sprite Mask适用場景分析
4. RectMask2D可能會破壞合批

有如下所示的一個測試場景,Panel1和Panel2都是隻挂有RectTransform元件的單純父節點,其下都有一個Image子節點,正常情況下應該可以合批,drawcall應為1

Unity遮罩之Mask、RectMask2D與Sprite Mask适用場景分析

通過幀調試器檢視,确實如此,成功合批,drawcall是1

Unity遮罩之Mask、RectMask2D與Sprite Mask适用場景分析

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

Unity遮罩之Mask、RectMask2D與Sprite Mask适用場景分析

通過幀調試器檢視,drawcall數量是2,原本的合批被破壞了。

Unity遮罩之Mask、RectMask2D與Sprite Mask适用場景分析

由此,網上查到的一些資料會得出“RectMask2D節點下的所有孩子都不能與外界UI節點合批且多個RectMask2D之間不能合批”的結論,實際上這是一種不嚴謹的說法,甚至是錯誤的。要搞清楚這個問題,需要先弄明白為什麼RectMask2D會破壞合批?

通過幀調試器可以發現,是RectMask2D傳遞裁剪矩形時,修改了Shader的參數,導緻不能合批。從下圖可以看到2次drawcall的差別就在于_ClipRect不同

Unity遮罩之Mask、RectMask2D與Sprite Mask适用場景分析
Unity遮罩之Mask、RectMask2D與Sprite Mask适用場景分析

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

Unity遮罩之Mask、RectMask2D與Sprite Mask适用場景分析

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

Unity遮罩之Mask、RectMask2D與Sprite Mask适用場景分析
5. Mask與RectMask2D用哪個?

Mask的實作利用了模闆緩沖區,會增加2個drawcall,性能會受到一定影響。簡單的UGUI界面,還是建議使用RectMask2D,相對來說性能更強,也無需額外的繪制調用。但由于RectMask2D也有可能破壞合批,在複雜的情況下,并沒有确切的結論來判斷哪個更優,隻能利用工具實際測試找到最優者,具體問題具體分析才是正确做法。當然,諸如圓形遮罩等一些RectMask2D無法勝任的場景,還是要使用Mask

粒子系統實作遮罩效果

遊戲的UI界面也經常會添加粒子效果,有時也會需要對粒子添加遮罩。Mask和RectMask2D隻适用于UGUI,對粒子系統無法生效。此時可以使用SpriteMask。SpriteMask的原理與Mask相同,都是基于模闆測試實作。

Unity遮罩之Mask、RectMask2D與Sprite Mask适用場景分析

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

Unity遮罩之Mask、RectMask2D與Sprite 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]
}
           

添加完成後,材質界面會多出模闆相關的配置,如下所示

Unity遮罩之Mask、RectMask2D與Sprite Mask适用場景分析

再配合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上的特效的裁剪問題

作者:iwiniwin

出處:http://www.cnblogs.com/iwiniwin/

本文為部落客原創文章,轉載請附上原文出處連結和本聲明。