UIGU源码分析13:MaskGraphic

源码13:MaskGraphic

public abstract class MaskableGraphic : Graphic, IClippable, IMaskable, IMaterialModifier
{
	...    
}

继承MaskGraphic 的有Image Text RawImage 基本可以使用显示的UI组件都继承了MaskGraphic

MaskableGraphic除了继承Graphic外 还继承了三个接口


IClippable

IClippable 被裁剪者 通常和裁剪者IClipper配套使用 看IClippable接口的源码注释

   public interface IClippable
    {
        /// <summary>
        ///可被裁剪的对象
        /// GameObject of the IClippable object
        /// </summary>
        GameObject gameObject { get; }

        /// <summary>
        ///可被裁剪的父对象状态发生变化的时候使用
        /// Will be called when the state of a parent IClippable changed.
        /// </summary>
        void RecalculateClipping();

        /// <summary>
        /// 可被裁剪的对象RectTransform
        /// The RectTransform of the clippable.
        /// </summary>
        RectTransform rectTransform { get; }

        /// <summary>
        /// 在给给定的裁剪范围中进行裁剪计算
        /// Clip and cull the IClippable given a specific clipping rect
        /// </summary>
        /// <param name="clipRect">The Rectangle in which to clip against.</param>
        /// <param name="validRect">Is the Rect valid. If not then the rect has 0 size.</param>
        void Cull(Rect clipRect, bool validRect);

        /// <summary>
        /// 设置可被裁剪的对象的裁剪范围
        /// Set the clip rect for the IClippable.
        /// </summary>
        /// <param name="value">The Rectangle for the clipping</param>
        /// <param name="validRect">Is the rect valid.</param>
        void SetClipRect(Rect value, bool validRect);

        /// <summary>
        /// Set the clip softness for the IClippable.
        ///
        /// The softness is a linear alpha falloff over clipSoftness pixels.
        /// </summary>
        /// <param name="clipSoftness">The number of pixels to apply the softness to </param>
        void SetClipSoftness(Vector2 clipSoftness);
        

IClipper 目前只有RectMask2D组件继承实现

也就是说通常裁剪的时候 是裁减者RectMask2D设置一个裁减矩形给被裁减者MaskableGraphic


这里先分析下MaskGraphic 是如何设置应用裁剪相关内容 如何裁剪在RectMask2D在分析

SetClipRect MaskableGraphic将裁减矩形发送到canvasRenderer渲染器中

public virtual void SetClipRect(Rect clipRect, bool validRect)
{
    if (validRect)
        canvasRenderer.EnableRectClipping(clipRect);
    else
        canvasRenderer.DisableRectClipping();
}

RecalculateClipping 重新计算获取父对象中的RectMask2D组件 (m_ParentMask)

/// <summary>
    /// See IClippable.RecalculateClipping
    /// </summary>
    public virtual void RecalculateClipping()
    {
        UpdateClipParent();
    }
    
    private void UpdateClipParent()
    {
    	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;
	}

Cull 剔除方法 如果validRect为false,或者输入的clipRect与所属Canvas的矩形区域不重合,调用UpdateCull方法,设置cull为true,把cull赋值给canvasRenderer.cull。如果canvasRenderer.cull发生变化时,发送事件m_OnCullStateChanged,m_OnCullStateChanged.Invoke(cull),并调用SetVerticesDirty,设置顶点的Dirty,等待重绘。

    public virtual void Cull(Rect clipRect, bool validRect)
    {
        var cull = !validRect || !clipRect.Overlaps(rootCanvasRect, true);
        UpdateCull(cull);
    }

    private void UpdateCull(bool cull)
    {
        if (canvasRenderer.cull != cull)
        {
            canvasRenderer.cull = cull;
            UISystemProfilerApi.AddMarker("MaskableGraphic.cullingChanged", this);
            m_OnCullStateChanged.Invoke(cull);
            OnCullingChanged();
        }
    }

IMaskable

IMaskable 被遮罩者 同IClippable类似也有个遮罩者 Mask组件(必须有一个MaskGraphic组件)

这里只分析作为IMaskable Mask后面再分析

IMaskable 只有一个实现方法RecalculateMasking

        public virtual void RecalculateMasking()
        {
            // Remove the material reference as either the graphic of the mask has been enable/ disabled.
            // This will cause the material to be repopulated from the original if need be. (case 994413)
            StencilMaterial.Remove(m_MaskMaterial);
            m_MaskMaterial = null;
            m_ShouldRecalculateStencil = true;
            SetMaterialDirty();
        }

设置m_ShouldRecalculateStencil为true,调用SetMaterialDirty 也就是启用Mask的时候,会让所有IMaskable子物体的StencilValue发生变化,并标记重建。

IMaterialModifier

MaskGraphic 也实现了IMaterialModifier接口,主要就是配合实现遮罩效果,因为遮罩效果正是通过材质实现的

        public virtual Material GetModifiedMaterial(Material baseMaterial)
        {
            var toUse = baseMaterial;

            if (m_ShouldRecalculateStencil)
            {
                if (maskable)
                {
                    var rootCanvas = MaskUtilities.FindRootSortOverrideCanvas(transform);
                    m_StencilValue = MaskUtilities.GetStencilDepth(transform, rootCanvas);
                }
                else
                    m_StencilValue = 0;

                m_ShouldRecalculateStencil = false;
            }

            // if we have a enabled Mask component then it will
            // generate the mask material. This is an optimization
            // it adds some coupling between components though :(
            if (m_StencilValue > 0 && !isMaskingGraphic)
            {
                var maskMat = StencilMaterial.Add(toUse, (1 << m_StencilValue) - 1, StencilOp.Keep, CompareFunction.Equal, ColorWriteMask.All, (1 << m_StencilValue) - 1, 0);
                StencilMaterial.Remove(m_MaskMaterial);
                m_MaskMaterial = maskMat;
                toUse = m_MaskMaterial;
            }
            return toUse;
        }

GetModifiedMaterial方法是再被标记重建后 在Rebulid中调用

如果需要重新计算模板,便从父RectTransform中获取overrideSorting为true的Canvas,赋值给rootCanvas,然后通过MaskUtilities.GetStencilDepth从rootCanvas获取模板深度。

如果模板深度大于0,且没有Mask组件或Mask组件没有激活,就把baseMaterial,stencilID,operation等参数添加到StencilMaterial中,并把之前旧的m_MaskMaterial从StencilMaterial中移除,用新的baseMaterial替换m_MaskMaterial,并返回


其他补充

  protected override void OnEnable()
    {
        base.OnEnable();
        m_ShouldRecalculateStencil = true;
        UpdateClipParent();
        SetMaterialDirty();

        if (isMaskingGraphic)
        {
            MaskUtilities.NotifyStencilStateChanged(this);
        }
    }

组件刚激活时 设置重新计算模板标记为True,更新被裁剪得父对象 设置Material为Dirty,SetMaterialDirty。从StencilMaterial移除了m_MaskMaterial,并设置m_MaskMaterial为空,如果Makk组件不为空,调用MaskUtilities.NotifyStencilStateChanged重新计算Mask。

   protected override void OnDisable()
    {
        base.OnDisable();
        m_ShouldRecalculateStencil = true;
        SetMaterialDirty();
        UpdateClipParent();
        StencilMaterial.Remove(m_MaskMaterial);
        m_MaskMaterial = null;

        if (isMaskingGraphic)
        {
            MaskUtilities.NotifyStencilStateChanged(this);
        }
    }

设置重新计算模板m_ShouldRecalculateStencil为true,更新裁剪的父对象UpdateClipParent,设置Material为Dirty,SetMaterialDirty。从StencilMaterial移除了m_MaskMaterial,并设置m_MaskMaterial为空,如果Mesh组件不为空,调用MaskUtilities.NotifyStencilStateChanged重新计算Mask。


StencilMaterial

    /// <summary>
    /// Dynamic material class makes it possible to create custom materials on the fly on a per-Graphic basis,
    /// and still have them get cleaned up correctly.
    /// </summary>
    public static class StencilMaterial
    {
       	private static List<MatEntry> m_List = new List<MatEntry>();
    	...
    }

StencilMaterial是一个静态类,负责管理模板材质。维护了一个MatEntry类型的列表:

private static List m_List = new List();

外部可以调用Add、Remove和ClearAll方法来对这个List进行操作。

Add方法,会创建一个MatEntry,并将输入的baseMat以及其他参数赋值给MatEntry,并创建了赋值baseMat的customMat,并将stencilID,operation等参数赋值给customMat,实际上赋值customMat的shader参数。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值