1. 程式人生 > >使用UGUI中的Mask的時候,如何正確裁減非預設材質的UI物件

使用UGUI中的Mask的時候,如何正確裁減非預設材質的UI物件

http://blog.csdn.net/ykmzy/article/details/51366396

在Unity中的UGUI中實現區域裁剪可以使用Mask元件,但是當改變被裁剪物件的材質後,裁剪效果可能會失效,也就是說Mask對該UI失效,這是為什麼呢? 
這我們得了解UGUI中Mask的工作原理。Mask得裁剪效果是使用Stencil計算來實現裁剪,關於Stencil(模板)可以仔細瞭解一下渲染管線得相關知識就可以知道是怎麼回事了,可參見維基百科,也可自行搜尋其它資料。 
通過參考Unity中UGUI中的Mask.cs原始碼,可以管窺一二。

/// Stencil calculation time!
public virtual Material GetModifiedMaterial(Material baseMaterial) { if (graphic == null) return baseMaterial; var rootSortCanvas = MaskUtilities.FindRootSortOverrideCanvas(transform); var stencilDepth = MaskUtilities.GetStencilDepth(transform, rootSortCanvas); if (stencilDepth >= 8
) { 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); StencilMaterial.Remove(m_MaskMaterial); m_MaskMaterial = maskMaterial; var unmaskMaterial = StencilMaterial.Add(baseMaterial, 1, StencilOp.Zero, CompareFunction.Always, 0); 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, m_ShowMaskGraphic ? ColorWriteMask.All : 0, desiredStencilBit - 1, desiredStencilBit | (desiredStencilBit - 1)); StencilMaterial.Remove(m_MaskMaterial); m_MaskMaterial = maskMaterial2; graphic.canvasRenderer.hasPopInstruction = true; var unmaskMaterial2 = StencilMaterial.Add(baseMaterial, desiredStencilBit - 1, StencilOp.Replace, CompareFunction.Equal, 0, desiredStencilBit - 1, desiredStencilBit | (desiredStencilBit - 1)); StencilMaterial.Remove(m_UnmaskMaterial); m_UnmaskMaterial = unmaskMaterial2; graphic.canvasRenderer.popMaterialCount = 1; graphic.canvasRenderer.SetPopMaterial(m_UnmaskMaterial, 0); return m_MaskMaterial; }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47

通過上面得程式碼我們得知Mask會對目標UI得材質使用StencilMaterial.Add方法來傳遞模板相關引數

StencilMaterial.Add(baseMaterial, desiredStencilBit - 1, StencilOp.Replace, CompareFunction.Equal, 0, desiredStencilBit - 1, desiredStencilBit | (desiredStencilBit - 1));
  • 1

繼續檢視StencilMaterial.Add方法,有如下程式碼

/// <summary>
        /// Add a new material using the specified base and stencil ID.
        /// </summary>
        public static Material Add(Material baseMat, int stencilID, StencilOp operation, CompareFunction compareFunction, ColorWriteMask colorWriteMask, int readMask, int writeMask)
        {
            if ((stencilID <= 0 && colorWriteMask == ColorWriteMask.All) || baseMat == null)
                return baseMat;

            if (!baseMat.HasProperty("_Stencil"))
            {
                Debug.LogWarning("Material " + baseMat.name + " doesn't have _Stencil property", baseMat);
                return baseMat;
            }
            if (!baseMat.HasProperty("_StencilOp"))
            {
                Debug.LogWarning("Material " + baseMat.name + " doesn't have _StencilOp property", baseMat);
                return baseMat;
            }
            if (!baseMat.HasProperty("_StencilComp"))
            {
                Debug.LogWarning("Material " + baseMat.name + " doesn't have _StencilComp property", baseMat);
                return baseMat;
            }
            if (!baseMat.HasProperty("_StencilReadMask"))
            {
                Debug.LogWarning("Material " + baseMat.name + " doesn't have _StencilReadMask property", baseMat);
                return baseMat;
            }
            if (!baseMat.HasProperty("_StencilReadMask"))
            {
                Debug.LogWarning("Material " + baseMat.name + " doesn't have _StencilWriteMask property", baseMat);
                return baseMat;
            }
            if (!baseMat.HasProperty("_ColorMask"))
            {
                Debug.LogWarning("Material " + baseMat.name + " doesn't have _ColorMask property", baseMat);
                return baseMat;
            }

            for (int i = 0; i < m_List.Count; ++i)
            {
                MatEntry ent = m_List[i];

                if (ent.baseMat == baseMat
                    && ent.stencilId == stencilID
                    && ent.operation == operation
                    && ent.compareFunction == compareFunction
                    && ent.readMask == readMask
                    && ent.writeMask == writeMask
                    && ent.colorMask == colorWriteMask)
                {
                    ++ent.count;
                    return ent.customMat;
                }
            }

            var newEnt = new MatEntry();
            newEnt.count = 1;
            newEnt.baseMat = baseMat;
            newEnt.customMat = new Material(baseMat);
            newEnt.customMat.hideFlags = HideFlags.HideAndDontSave;
            newEnt.stencilId = stencilID;
            newEnt.operation = operation;
            newEnt.compareFunction = compareFunction;
            newEnt.readMask = readMask;
            newEnt.writeMask = writeMask;
            newEnt.colorMask = colorWriteMask;
            newEnt.useAlphaClip = operation != StencilOp.Keep && writeMask > 0;

            newEnt.customMat.name = string.Format("Stencil Id:{0}, Op:{1}, Comp:{2}, WriteMask:{3}, ReadMask:{4}, ColorMask:{5} AlphaClip:{6} ({7})", stencilID, operation, compareFunction, writeMask, readMask, colorWriteMask, newEnt.useAlphaClip, baseMat.name);


            newEnt.customMat.SetInt("_Stencil", stencilID);
            newEnt.customMat.SetInt("_StencilOp", (int)operation);
            newEnt.customMat.SetInt("_StencilComp", (int)compareFunction);
            newEnt.customMat.SetInt("_StencilReadMask", readMask);
            newEnt.customMat.SetInt("_StencilWriteMask", writeMask);
            newEnt.customMat.SetInt("_ColorMask", (int)colorWriteMask);
            newEnt.customMat.SetInt("_UseAlphaClip", newEnt.useAlphaClip ? 1 : 0);
            m_List.Add(newEnt);
            return newEnt.customMat;
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82

通過對StencilMaterial.Add方法得檢視我們知道該方法在對目標材質得_StencilOp,_StencilComp等等屬性進行操作,這樣操作後才可以實現它得模板裁剪。我們自定義的Shader一般沒有這幾個屬性,所以我們在自己的shader中新增如下程式碼便可以實現自定義材質得正確Mask了

_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]
}