美文网首页
Mask源码简析

Mask源码简析

作者: CrazyTiger | 来源:发表于2018-05-06 23:03 被阅读0次

    版本

    • 源码版本:2017.3.0
    • 着色器版本:2017.3.0

    Mask使用方法

    https://jingyan.baidu.com/article/91f5db1b261d911c7f05e393.html

    层级关系

    MaskSample.png

    Graphic.UpdateMaterial()

    Image和Text都会设置材质到CanvasRenderer上。

    // Graphic.cs
            protected virtual void UpdateMaterial()
            {
                if (!IsActive())
                    return;
    
                canvasRenderer.materialCount = 1;
                canvasRenderer.SetMaterial(materialForRendering, 0);
                canvasRenderer.SetTexture(mainTexture);
            }
    

    Graphic.materialForRendering

    用IMaterialModifier来修改材质。Image找到的IMaterialModifier有Image和Mask。Text找到的IMaterialModifier只有Text。

    // Graphic.cs
            public virtual Material materialForRendering
            {
                get
                {
                    var components = ListPool<Component>.Get();
                    GetComponents(typeof(IMaterialModifier), components);
    
                    var currentMat = material;
                    for (var i = 0; i < components.Count; i++)
                        currentMat = (components[i] as IMaterialModifier).GetModifiedMaterial(currentMat);
                    ListPool<Component>.Release(components);
                    return currentMat;
                }
            }
    

    Mask.GetModifiedMaterial

    • 最多支持8层Mask,靠近Canvas最近的是第1层,次之第2层,以此类推。
    • 不同的层使用模板缓存不同的位。
      第1层Mask,写入0000 0001。
      第2层Mask,与第1层有交集的地方,写入0000 0011。
      第3层Mask,与第2层有交集的地方,写入0000 0111。
      .....
    • 通过CanvasRenderer.SetPopMaterial(),清空模板缓存。
      例子中Frame以及Frame的所有的ChildObject都渲染完毕之后,Frame的CanvasRenderer会清空模板缓存。
      再举个例子:
    Frame1(Mask + Image) // 第1层的Mask,写入0000 0001。
        Text1(Text) // 根据0000 0001进行模板测试。
            Frame2(Mask + Image) // 第2层的Mask,与第1层有交集的地方,写入0000 0011。
                Text2(Text) // 根据0000 0011进行模板测试
    
           // 在渲染Frame3之前,清空了0000 0010的模板缓存。
    
           Frame3(Mask + Image)  // 第2层的Mask,设置模板缓存的方式与Frame2相同。
               Text3(Text) // 模板测试的方式与Text2相同。
    
    // Mask.cs
            /// 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)
                {
                    // 模板缓存一共有8位,在Parent到Canvas之间已经有8个Mask了,当前Mask无法启用。
                    Debug.LogError("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 // ColorMask
                    );
                    StencilMaterial.Remove(m_MaskMaterial);
                    m_MaskMaterial = maskMaterial;
    
                    var unmaskMaterial = StencilMaterial.Add(
                          baseMaterial,
                          1, // 清空操作,模板参考值应该是没有作用了吧?
                          StencilOp.Zero,  // 清空Buffer
                          CompareFunction.Always, // 检测总是成功
                          0); // ColorMask
                    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, // 与上一层Mask的值相同才能通过检测
                      m_ShowMaskGraphic ? ColorWriteMask.All : 0,
                      desiredStencilBit - 1,  // ReadMask,
                      desiredStencilBit | (desiredStencilBit - 1)); // WriteMask
                StencilMaterial.Remove(m_MaskMaterial);
                m_MaskMaterial = maskMaterial2;
    
                 // 清除当前层,保留其他层。假设当前层是3。
                graphic.canvasRenderer.hasPopInstruction = true;
                var unmaskMaterial2 = StencilMaterial.Add(
                      baseMaterial, 
                      desiredStencilBit - 1, // RefValue = 0000 0011
                      StencilOp.Replace, 
                      CompareFunction.Equal, 
                      0, 
                      desiredStencilBit - 1,  // ReadMask = 0000 0011
                      desiredStencilBit | (desiredStencilBit - 1)); // WriteMask = 0000 0111
                StencilMaterial.Remove(m_UnmaskMaterial);
                m_UnmaskMaterial = unmaskMaterial2;
                graphic.canvasRenderer.popMaterialCount = 1;
                graphic.canvasRenderer.SetPopMaterial(m_UnmaskMaterial, 0);
    
                return m_MaskMaterial;
            }
    

    MaskableGraphic.GetModifiedMaterial

    • m_StencilValue = 在Parent到Canvas之间有效的Mask数量
    • 举例中的Image组件,因为与Mask在同一个GameObejct下面,Mask组件会修改Image组件的材质(修改模板缓存),Image就不再修改自己的材质了。
    • 举例中的Text组件,因为GameObject下没有Mask组件,并且Text的ParentGameObject到Canvas之间有Mask组件,所以要修改Text的材质(进行模板检测)。
    // MaskableGraphic.cs
            public virtual Material GetModifiedMaterial(Material baseMaterial)
            {
                var toUse = baseMaterial;
    
                if (m_ShouldRecalculateStencil)
                {
    
                    var rootCanvas = MaskUtilities.FindRootSortOverrideCanvas(transform);
                    m_StencilValue = maskable ? MaskUtilities.GetStencilDepth(transform, rootCanvas) : 0;
                    m_ShouldRecalculateStencil = false;
                }
    
                // if we have a enabled Mask component then it will
                // generate the mask material. This is an optimisation
                // it adds some coupling between components though :(
                Mask maskComponent = GetComponent<Mask>();
                if (m_StencilValue > 0 && (maskComponent == null || !maskComponent.IsActive()))
                {
                   // 例子中的Text组件,会进入到这里,会进行模板测试。
                    var maskMat = StencilMaterial.Add(
                         toUse,  // Material baseMat
                         (1 << m_StencilValue) - 1, // 参考值
                         StencilOp.Keep, // 不修改模板缓存
                         CompareFunction.Equal, // 相等通过测试
                         ColorWriteMask.All, // ColorMask
                         (1 << m_StencilValue) - 1, // Readmask
                         0); //  WriteMask
                    StencilMaterial.Remove(m_MaskMaterial);
                    m_MaskMaterial = maskMat;
                    toUse = m_MaskMaterial;
                }
                return toUse;
            }
    

    Shader代码

    // 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"
    
                #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;
                    UNITY_VERTEX_OUTPUT_STEREO
                };
    
                fixed4 _Color;
                fixed4 _TextureSampleAdd;
                float4 _ClipRect;
    
                v2f vert(appdata_t v)
                {
                    v2f OUT;
                    UNITY_SETUP_INSTANCE_ID(v);
                    UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);
                    OUT.worldPosition = v.vertex;
                    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);
                    #endif
    
                    #ifdef UNITY_UI_ALPHACLIP
                    clip (color.a - 0.001);
                    #endif
    
                    return color;
                }
            ENDCG
            }
        }
    }
    

    相关文章

      网友评论

          本文标题:Mask源码简析

          本文链接:https://www.haomeiwen.com/subject/upzerftx.html