/// Stencil calculation time! publicvirtual Material GetModifiedMaterial(Material baseMaterial) { if (!MaskEnabled()) return baseMaterial;
var rootSortCanvas = MaskUtilities.FindRootSortOverrideCanvas(transform); var stencilDepth = MaskUtilities.GetStencilDepth(transform, rootSortCanvas); if (stencilDepth >= 8) { Debug.LogWarning("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);//核心方法,为了开始模板缓存,需要给Mask一个特殊的材质,这里就增加了一次drawcall,这也是Mask打断合批的原因 StencilMaterial.Remove(m_MaskMaterial); m_MaskMaterial = 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;
publicvirtualvoidPerformClipping() { if (ReferenceEquals(Canvas, null)) { return; }
//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) { MaskUtilities.GetRectMasksForClip(this, m_Clippers);//通过这里获取Rect m_ShouldRecalculateClipRects = false; }
// get the compound rects from // the clippers that are valid bool validRect = true; Rect clipRect = Clipping.FindCullAndClipWorldRect(m_Clippers, out validRect);//计算出需要剪切的区域
// If the mask is in ScreenSpaceOverlay/Camera render mode, its content is only rendered when its rect // overlaps that of the root canvas. RenderMode renderMode = Canvas.rootCanvas.renderMode; bool maskIsCulled = (renderMode == RenderMode.ScreenSpaceCamera || renderMode == RenderMode.ScreenSpaceOverlay) && !clipRect.Overlaps(rootCanvasRect, true);
if (maskIsCulled) { // Children are only displayed when inside the mask. If the mask is culled, then the children // inside the mask are also culled. In that situation, we pass an invalid rect to allow callees // to avoid some processing. clipRect = Rect.zero; validRect = false; }
if (clipRect != m_LastClipRectCanvasSpace) { foreach (IClippable clipTarget in m_ClipTargets) { clipTarget.SetClipRect(clipRect, validRect);//被剪切的目标自己执行SetClipRect方法 }
foreach (MaskableGraphic maskableTarget in m_MaskableTargets) { maskableTarget.SetClipRect(clipRect, validRect);
if (maskableTarget.canvasRenderer.hasMoved) maskableTarget.Cull(clipRect, validRect); } } else { foreach (MaskableGraphic maskableTarget in m_MaskableTargets) { //Case 1170399 - hasMoved is not a valid check when animating on pivot of the object maskableTarget.Cull(clipRect, validRect); } }