type
status
date
summary
tags
category
icon

一、概述

Canvas是UI的最上层,负责管理下面子节点UI元素的布局、渲染、排序。在UGUI中得到实现的类是CanvasScalerGraphicRaycaster,而Canvas, CanvasGroup, CanvasRenderer并不在UGUI中实现。
本文记录了Canvas相关组件的作用,通过对CanvasUpdateRegistry类的代码进行剖析来介绍Canvas渲染前会经历哪些步骤,介绍展示的Unity源码版本基于2019.1,链接地址如下:

二、基础组件

2.1 Canvas

Unity官方文档对Canvas组件的描述如下:
🔹
画布 (Canvas) 组件表示进行 UI 布局和渲染的抽象空间。所有 UI 元素都必须是附加了画布组件的游戏对象的子对象
可以看出,Canvas就相当于画画时的画板,UI元素可以理解为画板上的内容,当我们将各类元素画好后,Canvas要做的事情就是合并这些元素。
合并的规则为:同一个Canvas里,相同层级,相同材质球的元素进行合并,从而减少Drawcall。不过相同层级的概念并不是gameobject上的节点层级,而是覆盖层级。如果两个元素重叠,则可以认为它们是上下层关系,把所有重叠的层级数计算完毕后,第0层的所有元素统一合并,第1层的元素也统一合并,以此类推。

Render Mode

Overlay
notion image
Overlay模式并不与空间上排序有任何关系,空间上的前后位置不再对元素起作用,它常用在纯UI的区域内。在此模式下,画布被缩放以适合屏幕,然后直接渲染而不参考场景或相机(即使场景中根本没有相机,也会呈现UI)
Sort order参数在排序时被着重使用到,Sort order参数的值越大,越靠前渲染。在这个模式下没有Camera的渲染机制因此很难加入普通的3D模型物体来增加效果。
该模式下,Canvas组件需要在UI层次结构的顶层,不然UI会在视图中消失。
Screen Camera
notion image
Screen Camera模式相对比较通用一点,它依赖于Camera的平面透视,渲染时的布局依赖于它绑定的Camera。场景中比UI平面更靠近相机的任何3D对象都将在UI前面渲染,而平面后面的对象将被遮挡。
这种模式是实际项目中制作UI最常用的模式,不过UGUI底层有对排序做些规则,如对元素的z轴不为0的元素,会单独提取出来渲染,不参与合并
World Space
notion image
World Space模式主要用于当UI物体放在3D世界中的场景下。比如,一个大的场景中,需要将一张标志图放在一个石块头上,这时就需要World Space模式。
它与 Screen Camera 的区别是,它常在世界空间中与普通3D物体一同展示,依赖于截锥体透视(Perspective)Camera。画布的大小可以使用其矩形变换进行设置,但其在屏幕上的大小将取决于摄像机的视角和距离。它的原理挺简单的,与普通物体一样,当UI物体在这个Camera视野中时,就相当于渲染了一个普通的3D面片,只不过除了普通的渲染Canvas还对这些场景里的UI进行合并处理。

2.2 GraphicRaycaster

notion image
输入系统的图形碰撞测试组件,它并不会检测Canvas以外的内容,检测的都是画布下的元素。当图元素上存在有效的碰撞体时,Graphic Raycaster 组件会统一使用射线碰撞测试来检测碰撞的元素。
参数
含义
Ignore Reversed Graphics
忽略反转背对相机的UI元素的射线检测
Blocking Objects
可以阻断射线检测的物体类型,2D\3D\所有,要求这类物体有Collider组件。在Camera Space 和World Space能体现出来,UI射线检测是否可以穿透3D物体
Blocking Mask
可以阻断射线检测的物体的layer

2.3 CanvasScaler

notion image
这是个缩放比例组件,用来指定画布中元素的比例大小。
有简单指定比例大小的Constant Pixel Size模式,也有Scale With Screen Size以屏幕为基准的自动适配比例大小,或者Constant Physical Size以物理大小为基准的适配规则。 在实际手游项目里,设备的屏幕分辨率变化比较大,通常使用以屏幕为基准的自动适配比例大小的Scale With Screen Size选项

2.4 CanvasGroup

CanvasGroup用于从一个位置控制它自己和所有子项对象。Canvas Group参数改动时会给下面的UI元素(继承了UIBehaviour)发送OnCanvasGroupChanged消息。
notion image
参数
含义
Alpha
group下的UI元素透明度基数,会和UI元素本身的alpha值相乘
Interactable
group下的UI元素是否可交互(射线检测),取消勾选意味着是单纯的HUD
Block Raycasts
group下面的UI是否会阻断射线检测,在Camera Space或World Space的canvas可以阻断射线穿透UI到后面的3D物体
Ignore Parent Groups
忽略父canvas group的设置,用这个canvas group的设置覆盖

2.5 CanvasRender

CanvasRenderer负责渲染继承自Graphic类的UI元素(Image、RawImage、Text),每个元素都需要一个CanvasRenderer,其他UI元素可以不添加CanvasRenderer组件。
CanvasRenderer会做UI基于canvas的剔除、网格提交、材质绑定、渲染,本质上和MeshRenderer做的内容类似。

三、源码剖析

Canvas是UI的最上层,管理下面UI元素的布局和渲染。涉及Canvas类、CanvasUpdateRegistry类、ICanvasElement接口和CanvasUpdate枚举。核心是CanvasUpdateRegistry
notion image
图片引用

3.1 ICanvasElement

在Unity中,几乎所有的UI组件都继承自ICanvasElement接口,接口内提供了重建UI组件的Rebuild方法,继承自该接口的组件都会对该方法进行重写。
notion image
ICanvasElement接口包含的方法如下:
可以看到,Rebuild方法中需要提供的参数类型为CanvasUpdate,这是一个枚举类型。在UI元素实现的Rebuild方法中,方法会将传入的枚举类型通过switch来对不同阶段做不同的处理。之所以要分阶段,是因为UI无论布局还是网格材质的更新,都需要按照一定顺序才能保证结果正确。

3.2 CanvasUpdateRegistry

  1. Canvas向外提供了事件willRenderCanvases,该事件会在渲染前每帧都执行。CanvasUpdateRegistry在构造函数中向该事件注册了PerformUpdate方法,该方法是整个UGUI布局和渲染更新前的入口需要注意的是,该方法并不会对UGUI中的元素进行渲染操作
    1. CanvasUpdateRegistry中使用两个索引集IndexedSet(内部使用List和Dictionary实现)来保存需要的UI元素。可能引起好奇的是,UI元素在什么条件会被添加进这些队列中呢?这里以m_GraphicRebuildQueue队列举例,继承自Graphic类的UI元素(Text, Image, RawImage)在下面的情况下都会被添加进队列(大部分回调接口都是继承自UIBehaviour):
      1. OnRectTransformDimensionsChange
      2. OnTransformParentChanged
      3. OnDidApplyAnimationProperties
      4. OnCanvasHierarchyChanged
      5. OnCullingChanged
    1. 需要更新的UI元素将会在PerformUpdate方法中,通过ICanvasElement被重写的Rebuild方法进行更新。

      四、引用

      Unity UGUI模块 Graphic类和UI重建(Rebuild)Unity UGUI模块 Mask和RectMask2D
      • Twikoo
      • Cusdis