RectMask2D源碼簡(jiǎn)析

0. 版本

  • 源碼版本:2017.3.0
  • 著色器版本:2017.3.0

1. PerformClipping調(diào)用順序

  • CanvasUpdateRegistry.PerformUpdate()
    public class CanvasUpdateRegistry
    {
        protected CanvasUpdateRegistry()
        {
            // 每一幀都會(huì)調(diào)用
            Canvas.willRenderCanvases += PerformUpdate;
        }
        
        private void PerformUpdate()
        {
             // 更新Layout
             ......

             // now layout is complete do culling...
             ClipperRegistry.instance.Cull();

             // 更新Graphic
             ......
        }
    }
  • ClipperRegistry.Cull()
    public class ClipperRegistry
    {
        // RectMask2D實(shí)現(xiàn)了IClipper接口
        readonly IndexedSet<IClipper> m_Clippers = new IndexedSet<IClipper>();
        
        public void Cull()
        {
            for (var i = 0; i < m_Clippers.Count; ++i)
            {
                m_Clippers[i].PerformClipping();
            }
        }
    }
  • RectMask2D.PerformClipping()
    public class RectMask2D : UIBehaviour, IClipper, ICanvasRaycastFilter
    {
        private List<RectMask2D> m_Clippers = new List<RectMask2D>();

        // MaskableGraphic實(shí)現(xiàn)了IClippable接口
        private List<IClippable> m_ClipTargets = new List<IClippable>();
        
        public virtual void PerformClipping()
        {
            //TODO See if an IsActive() test would work well here or whether it might cause unexpected side effects (re case 776771)

            // if the parents are changed
            // or something similar we
            // do a recalculate here
            if (m_ShouldRecalculateClipRects)
            {
                // m_Clippers = this的Parent路徑上的暑塑,所有的RectMask2D組件。
                MaskUtilities.GetRectMasksForClip(this, m_Clippers);
                m_ShouldRecalculateClipRects = false;
            }

            // get the compound rects from
            // the clippers that are valid
            bool validRect = true;
            // clipRect = m_Clippers中所有的RectMask2D的區(qū)域的交集。
            // vaildRect = 區(qū)域是否有交集秒咐。
            Rect clipRect = Clipping.FindCullAndClipWorldRect(m_Clippers, out validRect);
            bool clipRectChanged = clipRect != m_LastClipRectCanvasSpace;
            if (clipRectChanged || m_ForceClip)
            {
                // 重點(diǎn)1:向MaskGraphic設(shè)置剪裁區(qū)域。
                // m_ClipTargets有哪些后面會(huì)分析注竿。
                foreach (IClippable clipTarget in m_ClipTargets)
                    clipTarget.SetClipRect(clipRect, validRect);

                m_LastClipRectCanvasSpace = clipRect;
                m_LastValidClipRect = validRect;
            }

            foreach (IClippable clipTarget in m_ClipTargets)
            {
                // hasMoved : True if any change has occured that would invalidate the positions of generated geometry. 
                var maskable = clipTarget as MaskableGraphic;
                if (maskable != null && !maskable.canvasRenderer.hasMoved && !clipRectChanged)
                    continue;

                // 重點(diǎn)2:MaskGraphic進(jìn)行剪裁搀罢。  
                clipTarget.Cull(m_LastClipRectCanvasSpace, m_LastValidClipRect);
            }
        }
  • MaskGraphic.SetClipRect
  • MaskGraphic.Cull
    public abstract class MaskableGraphic : Graphic, IClippable, IMaskable, IMaterialModifier
    {       
        public virtual void SetClipRect(Rect clipRect, bool validRect)
        {
            if (validRect)  // Mask有交接,設(shè)置區(qū)域赤拒。修改材質(zhì)的事被CanvasRenderer做了,黑盒看不到诱鞠。
                canvasRenderer.EnableRectClipping(clipRect);
            else
                canvasRenderer.DisableRectClipping(); // Mask沒有交集挎挖。
        }
        
        public virtual void Cull(Rect clipRect, bool validRect)
        {
            var cull = !validRect || !clipRect.Overlaps(rootCanvasRect, true);
            UpdateCull(cull);
        }

        private void UpdateCull(bool cull)
        {
            var cullingChanged = canvasRenderer.cull != cull;
            canvasRenderer.cull = cull;

            if (cullingChanged)
            {
                UISystemProfilerApi.AddMarker("MaskableGraphic.cullingChanged", this);
                m_OnCullStateChanged.Invoke(cull);
                SetVerticesDirty(); // 疑問:為什么cull變了,要重建網(wǎng)格航夺?猜測(cè)是CanvasRender根據(jù)cull的情況蕉朵,做了網(wǎng)格的優(yōu)化,減少了一些Overdraw阳掐∈夹疲可以實(shí)驗(yàn)看看。
            }
        }
    }

2. 剪裁區(qū)域

  • GetCanvasRect
    internal class RectangularVertexClipper
    {
        readonly Vector3[] m_WorldCorners = new Vector3[4];
        readonly Vector3[] m_CanvasCorners = new Vector3[4];

        public Rect GetCanvasRect(RectTransform t, Canvas c)
        {
            if (c == null)
                return new Rect();
            
            t.GetWorldCorners(m_WorldCorners);
            var canvasTransform = c.GetComponent<Transform>();
            for (int i = 0; i < 4; ++i)
                m_CanvasCorners[i] = canvasTransform.InverseTransformPoint(m_WorldCorners[i]);

            return new Rect(m_CanvasCorners[0].x, m_CanvasCorners[0].y, m_CanvasCorners[2].x - m_CanvasCorners[0].x, m_CanvasCorners[2].y - m_CanvasCorners[0].y);
        }
    }
  • UI/Default
    疑問:OUT.worldPosition = v.vertex; 讀取的是Local坐標(biāo)缭保,怎么是World坐標(biāo)呢汛闸?
    猜測(cè):CanvasBuildBatch生成的Mesh,這個(gè)Mesh直接放到世界坐標(biāo)系下了艺骂,Local坐標(biāo)是World坐標(biāo)是一樣的诸老。
// Unity built-in shader source. Copyright (c) 2016 Unity Technologies. MIT license (see license.txt)

Shader "UI/Default"
{
    Properties
    {
        [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
        _Color ("Tint", Color) = (1,1,1,1)

        _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

        _ColorMask ("Color Mask", Float) = 15

        [Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip ("Use Alpha Clip", Float) = 0
    }

    SubShader
    {
        Tags
        {
            "Queue"="Transparent"
            "IgnoreProjector"="True"
            "RenderType"="Transparent"
            "PreviewType"="Plane"
            "CanUseSpriteAtlas"="True"
        }

        Stencil
        {
            Ref [_Stencil]
            Comp [_StencilComp]
            Pass [_StencilOp]
            ReadMask [_StencilReadMask]
            WriteMask [_StencilWriteMask]
        }

        Cull Off
        Lighting Off
        ZWrite Off
        ZTest [unity_GUIZTestMode]
        Blend SrcAlpha OneMinusSrcAlpha
        ColorMask [_ColorMask]

        Pass
        {
            Name "Default"
        CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma target 2.0

            #include "UnityCG.cginc"
            #include "UnityUI.cginc" // 2D Mask 剪裁。

            #pragma multi_compile __ UNITY_UI_CLIP_RECT
            #pragma multi_compile __ UNITY_UI_ALPHACLIP

            struct appdata_t
            {
                float4 vertex   : POSITION;
                float4 color    : COLOR;
                float2 texcoord : TEXCOORD0;
                UNITY_VERTEX_INPUT_INSTANCE_ID
            };

            struct v2f
            {
                float4 vertex   : SV_POSITION;
                fixed4 color    : COLOR;
                float2 texcoord  : TEXCOORD0;
                float4 worldPosition : TEXCOORD1; // 2D Mask 剪裁彻亲。
                UNITY_VERTEX_OUTPUT_STEREO
            };

            fixed4 _Color;
            fixed4 _TextureSampleAdd;
            float4 _ClipRect; // 2D Mask 剪裁孕锄。

            v2f vert(appdata_t v)
            {
                v2f OUT;
                UNITY_SETUP_INSTANCE_ID(v);
                UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);
                OUT.worldPosition = v.vertex;  // 2D Mask 剪裁吮廉。
                OUT.vertex = UnityObjectToClipPos(OUT.worldPosition);

                OUT.texcoord = v.texcoord;

                OUT.color = v.color * _Color;
                return OUT;
            }

            sampler2D _MainTex;

            fixed4 frag(v2f IN) : SV_Target
            {
                half4 color = (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd) * IN.color;

                #ifdef UNITY_UI_CLIP_RECT
                color.a *= UnityGet2DClipping(IN.worldPosition.xy, _ClipRect); // 2D Mask 剪裁苞尝。
                #endif

                #ifdef UNITY_UI_ALPHACLIP
                clip (color.a - 0.001);
                #endif

                return color;
            }
        ENDCG
        }
    }
}

3. m_ClipTargets

// MaskGraphic.cs
        protected override void OnEnable()
        {
            ......
            UpdateClipParent();
            ......
        }

        protected override void OnDisable()
        {
            ......
            UpdateClipParent();
            ......
        }

        protected override void OnTransformParentChanged()
        {
            ......
            UpdateClipParent();
            ......
        }
        protected override void OnCanvasHierarchyChanged()
        {
            ......
            UpdateClipParent();
            ......
        }
        public virtual void RecalculateClipping()
        {
            UpdateClipParent();
        }
// MaskGraphic.cs

        private void UpdateClipParent()
        {
            // 返回最近的ParentMask
            var newParent = (maskable && IsActive()) ? MaskUtilities.GetRectMaskForClippable(this) : null;

            // if the new parent is different OR is now inactive
            if (m_ParentMask != null && (newParent != m_ParentMask || !newParent.IsActive()))
            {
                m_ParentMask.RemoveClippable(this);
                UpdateCull(false);
            }

            // don't re-add it if the newparent is inactive
            if (newParent != null && newParent.IsActive())
                newParent.AddClippable(this);

            m_ParentMask = newParent;
        }
// RectMask2D.cs

        public void AddClippable(IClippable clippable)
        {
            if (clippable == null)
                return;
            m_ShouldRecalculateClipRects = true;
            if (!m_ClipTargets.Contains(clippable))
                m_ClipTargets.Add(clippable);

            m_ForceClip = true;
        }

4. 總結(jié)

  • RectMask2D在每幀都會(huì)檢查剪裁區(qū)域是否變化了。PerformUpdate中執(zhí)行宦芦。
  • 剪裁區(qū)域 = 當(dāng)前RectMask2D到Parent路徑上的所有有效的RectMask2D的剪裁區(qū)域的交集宙址。
  • 如果剪裁區(qū)域變化了,會(huì)通過CanvasRenderer來間接修改MaskGraphic的材質(zhì)參數(shù)调卑,把剪裁區(qū)域傳進(jìn)去抡砂。
  • 如果剪裁區(qū)域變化了,還會(huì)為MaskGraphic調(diào)用SetVerticesDirty恬涧,重新生成網(wǎng)格注益。猜測(cè)是CanvasRenderer對(duì)剪裁的網(wǎng)格進(jìn)行了優(yōu)化,避免了OverDraw溯捆。代價(jià)就是剪裁區(qū)域變化的時(shí)候丑搔,要重新生成網(wǎng)格。
  • 每個(gè)MaskGraphic,會(huì)把自己注冊(cè)到啤月,最近的一個(gè)ParentMask上面煮仇。
  • OUT.worldPosition = v.vertex; 讀取的是Local坐標(biāo)。猜測(cè)CanvasBuildBatch生成的Mesh谎仲,這個(gè)Mesh直接放到世界坐標(biāo)系下了浙垫,Local坐標(biāo)是World坐標(biāo)是一樣的。
  • 放到CanvasRenderer里面的是Local坐標(biāo)郑诺,懷疑CanvasRenderer夹姥,把這個(gè)Local坐標(biāo)又轉(zhuǎn)成World坐標(biāo),再放到_ClipRect里的间景。根據(jù)一些測(cè)試佃声,直接跳過CanvasRenderer,向_ClipRect直接傳入World坐標(biāo)是對(duì)的倘要,傳入Local坐標(biāo)是不對(duì)的圾亏。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市封拧,隨后出現(xiàn)的幾起案子志鹃,更是在濱河造成了極大的恐慌,老刑警劉巖泽西,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件曹铃,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡捧杉,警方通過查閱死者的電腦和手機(jī)陕见,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來味抖,“玉大人评甜,你說我怎么就攤上這事∽猩” “怎么了忍坷?”我有些...
    開封第一講書人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)熔脂。 經(jīng)常有香客問我佩研,道長(zhǎng),這世上最難降的妖魔是什么霞揉? 我笑而不...
    開封第一講書人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任旬薯,我火速辦了婚禮,結(jié)果婚禮上适秩,老公的妹妹穿的比我還像新娘绊序。我一直安慰自己些侍,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開白布政模。 她就那樣靜靜地躺著岗宣,像睡著了一般。 火紅的嫁衣襯著肌膚如雪淋样。 梳的紋絲不亂的頭發(fā)上耗式,一...
    開封第一講書人閱讀 51,292評(píng)論 1 301
  • 那天,我揣著相機(jī)與錄音趁猴,去河邊找鬼刊咳。 笑死,一個(gè)胖子當(dāng)著我的面吹牛儡司,可吹牛的內(nèi)容都是我干的娱挨。 我是一名探鬼主播,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼捕犬,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼跷坝!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起碉碉,我...
    開封第一講書人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤柴钻,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后垢粮,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體贴届,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年蜡吧,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了毫蚓。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡昔善,死狀恐怖元潘,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情耀鸦,我是刑警寧澤柬批,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布啸澡,位于F島的核電站袖订,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏嗅虏。R本人自食惡果不足惜洛姑,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望皮服。 院中可真熱鬧楞艾,春花似錦参咙、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至两入,卻和暖如春净宵,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背裹纳。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工择葡, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人剃氧。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓敏储,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親朋鞍。 傳聞我的和親對(duì)象是個(gè)殘疾皇子已添,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • 更新:【面試題含答案】http://bbs.9ria.com/thread-288394-1-1.html 高頻問...
    好怕怕閱讀 4,747評(píng)論 3 52
  • 一:什么是協(xié)同程序?答:在主線程運(yùn)行時(shí)同時(shí)開啟另一段邏輯處理滥酥,來協(xié)助當(dāng)前程序的執(zhí)行酝碳。換句話說,開啟協(xié)程就是開啟一個(gè)...
    CrixalisAs閱讀 2,074評(píng)論 1 7
  • Information about the pose, topology, and expression of a...
    loveFBI閱讀 2,179評(píng)論 0 2
  • 《ilua》速成開發(fā)手冊(cè)3.0 官方用戶交流:iApp開發(fā)交流(1) 239547050iApp開發(fā)交流(2) 1...
    葉染柒丶閱讀 10,720評(píng)論 0 11
  • Information about the position and orientation of a real-...
    loveFBI閱讀 1,241評(píng)論 0 0