NGUI渲染流程

0. 概述

本文将从整体类图出发,先对NGUI渲染涉及到几个重点的类的关系有一个整体的了解,接着再讲下各个类的作用,然后通过源码将下整个渲染的流程,最后尝试解答几个问题。本文使用的NGUI版本是3.8.2。

1. 整体类图

NGUI_Class.png
我们从图中可以看到涉及到NGUI渲染流程的类主要有UIRect、UIWidget、UIPanel、UIDrawcall和UIGeometry。

2. 各个类的作用

2.1 UIRect

UIRect作为UIWidget和UIPanel的抽象基类,主要作用是维护4个锚点(左/上/右/下),并根据锚定更新类型在适当的时候更新4个锚点,并提供OnAnchor抽象方法让子类根据锚点更新自己的尺寸。

2.2 UIGeometry

  • UIGeometry由UIWidget生成,UIWidget都有一个UIGeometry
  • UIGeometry包含Widget的顶点verts,几何顶点的纹理坐标uvs,几何顶点的颜色cols,并提供接口将顶点数据转换为所属Panel局部坐标的顶点mRtpVerts,法线mRtpNormal和切线数据mRtpTan。
  • UIGeometry将几何数据生成分开为3个步骤,保证widget只有在自己发生变化的时候重build
    • 1.清空缓存-对应方法Clear
      1. 根据世界坐标到panel本地坐标的矩阵将转换Widget的顶点数据- 对应方法ApplyTransform
      1. 向特定传入的buffer填入转换后的顶点等数据-对应方法WriteToBuffers

2.3 UIWidget

  • UIWidget是每个UI组件如我们常见的UITexture、UISprite、UILabel的基类,每个UI元素对应一个UIWidget。
  • UIWidget包含UI元素信息如width、height、depth、pivot、alpha、color,并提供几个抽象属性material、mainTexture和shader由子类
    去实现。
  • UIWidget都有一个UIGeometry,并提供***OnFill***抽象方法,由子类如UILabel和UISprite将顶点、UV、Color、法线和切线数据填入UIGeometry缓存中。
  • UIWidget都有一个UIDrawcall,但一个UIDrawcall对应1个或者多个的UIWidget,UI元素的渲染最终是通过UIDrawcall实现的。

2.4 UIDrawCall

  • UIDrawcall是渲染UI元素的载体,由UIPanel生成,其中包含渲染使用的MeshRender、Mesh、MeshFilter和Material组件。
  • 其核心函数是UpdateGeometry,其分为以下几个步骤进行几何信息更新:1. 生成Mesh,2.赋值缓存的顶点、UV、颜色、法线和切线等数据给Mesh,3.更新MeshRender使用的材质

2.5 UIPanel

  • UIPanel作为UIWidget列表和UIDrawcall列表的管理者,监测UIPanel下的Widget的变化,一旦Widget发生变化就需要更新widget对应的drawcall。在必要的时候需要重新生成全部的drawcall。
  • UIPanel的UIWidget列表是按照widget的深度排序的
  • UIPanel维护一个静态的panel列表,是按照panel的深度排序的

3. 渲染流程

[图片上传中…(NGUI_UIPanel.png-f91a26-1571404641111-0)]

3.1 UIPanel的LateUpdate

先遍历根据深度进行排序的静态的Panel列表,并调用UIPanel的UpdateSelf函数更新每个Panel.
再遍历Panel列表根据Panel的RenderQueue类型不同(Automatic/StartAt/Explicit),设置相应的RenderQueue数据并调用Panel的UpdateDrawCalls更新Drawcall。

void LateUpdate ()
	{
#if UNITY_EDITOR
		if (mUpdateFrame != Time.frameCount || !Application.isPlaying)
#else
		if (mUpdateFrame != Time.frameCount)
#endif
		{
			mUpdateFrame = Time.frameCount;

			// Update each panel in order
			for (int i = 0, imax = list.Count; i < imax; ++i)
				list[i].UpdateSelf();

			int rq = 3000;

			// Update all draw calls, making them draw in the right order
			for (int i = 0, imax = list.Count; i < imax; ++i)
			{
				UIPanel p = list[i];

				if (p.renderQueue == RenderQueue.Automatic)
				{
					p.startingRenderQueue = rq;
					p.UpdateDrawCalls();
					rq += p.drawCalls.Count;
				}
				else if (p.renderQueue == RenderQueue.StartAt)
				{
					p.UpdateDrawCalls();
					if (p.drawCalls.Count != 0)
						rq = Mathf.Max(rq, p.startingRenderQueue + p.drawCalls.Count);
				}
				else // Explicit
				{
					p.UpdateDrawCalls();
					if (p.drawCalls.Count != 0)
						rq = Mathf.Max(rq, p.startingRenderQueue + 1);
				}
			}
		}
	}

3.2 UIPanel的UpdateSelf

3.2.1 流程

  • UpdateTransformMatrix
    更新世界坐标到Panel本地坐标的矩阵和裁剪区域
  • UpdateLayers
    更新widget layer,保持widget的layer与panel一致
  • UpdateWidgets
    更新属于Panel下的所有widget
    • 判断是否重Build
      • 是 调用UIPanel的FillAllDrawCalls重新生成Drawcall
      • 否 遍历Drawcall列表如果Drawcall.isDirty就调用FillDrawCall更新对应Drawcall

3.2.2 代码

void UpdateSelf ()
	{
		mUpdateTime = RealTime.time;

		UpdateTransformMatrix();
		UpdateLayers();
		UpdateWidgets();

		if (mRebuild)
		{
			mRebuild = false;
			FillAllDrawCalls();
		}
		else
		{
			for (int i = 0; i < drawCalls.Count; )
			{
				UIDrawCall dc = drawCalls[i];

				if (dc.isDirty && !FillDrawCall(dc))
				{
					UIDrawCall.Destroy(dc);
					drawCalls.RemoveAt(i);
					continue;
				}
				++i;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值