UGUI源码试探究 (一) 图形绘制

UGUI源码试探究 (一)

1. 前言

1.1 UGUI是什么

  • UGUI是Unity官方推出的UI系统, 集成了所见即所得的UI解决方案, 其功能丰富并且使用简单, 同时其部分源代码也是开放的

  • 下载地址:https://bitbucket.org/Unity-Technologies/ui/src

2. 图形绘制

2.1 绘制基础

Mesh:

  • 通俗的讲, Mesh是指模型的网格, 3D模型是由多边形拼接而成, 而一个复杂的多边形, 实际上是由多个三角形拼接而成. 所以一个3D模型的表面是由多个彼此相连的三角形构成. 三维空间中, 构成这些三角形的点以及三角形的边的集合就是Mesh. 如下图所示:

这里写图片描述

Vertex:

  • 顶点, 组成Mesh的元素. 一个三角面有3个顶点, 如下图:

这里写图片描述

Triangle:

  • 三角形, 通过指定顶点索引顺序来构成三角形

  • 三角形的顶点顺序为顺时针

  • 在下图中, 构成M和N的顶点顺序可以为: (A, B, C), (C, D A)

这里写图片描述

UV:

  • 纹理贴图坐标

  • 顶点属性中储存UV坐标, 指的是该点对应纹理的位置

  • UV的坐标轴值范围为(0, 1), 与纹理的大小比例无关

这里写图片描述

VertexHelper:

  • 一个Unity提供的帮助类, 用于帮助生成Mesh

  • 主要方法说明:

    • AddVert: 向顶点缓冲流中加入一个顶点, 包括顶点的属性(位置, 颜色, UV坐标等)

    • AddTriangle: 向索引缓冲流中加入一个三角形, 参数为构成三角形的顶点的索引(注意顺序为顺时针)

    • FillMesh: 将VertexHelper中的缓存数据填入到指定的Mesh网格中

2.2 绘制基础实例

下面通过使用VertexHelper和射线检测来实现可动态变化的简易Image

最终效果如图:

这里写图片描述

2.2.0 组件需求

如果我们在场景中创建一个Cube, 会发现Cube带了三个组件

这里写图片描述

  • MeshFilter : 储存资源的Mesh, 可以由MeshRenderer在屏幕上渲染出来

  • BoxCollider: 碰撞器, 可以接受射线的检测

  • MeshRenderer: 从MeshFilter中提取中几何体并渲染出来

因此, 我们新建一个空的GameObject(在Canvas子目录中), 添加三个组件MeshFilter, MeshRenderer, MeshCollider

2.2.1 新建MyImage.cs并挂载到GameObject上

主要步骤为:

  1. 脚本获得挂载的组件

    mMyMeshFilter = GetComponent<MeshFilter>();
    mMyMeshRenderer = GetComponent<MeshRenderer>();
    mMyMeshCollider = GetComponent<MeshCollider>();
    
  2. 添加顶点信息

    mVertexHelpers[0].AddVert(Vector3.down * imageSize, Color.white, new Vector2(0, 0));
    mVertexHelpers[0].AddVert(Vector3.left * imageSize, Color.white, new Vector2(0, 1));
    mVertexHelpers[0].AddVert(Vector3.right * imageSize, Color.white, new Vector2(1, 0));
    
  3. 添加三角形信息

    mVertexHelpers[0].AddTriangle(0, 1, 2);
    
  4. 指定MeshFilter.mesh

    mMyMeshFilter.mesh = mMyMesh;
    
  5. 指定MeshRenderer.material.shader

    mMyMeshRenderer.material.shader = Shader.Find("Unlit/Texture");
    
  6. 指定MeshRenderer.material.mainTexture

    mMyMeshRenderer.material.mainTexture = texture2D;
    
  7. 指定MeshCollider.sharedMesh

    mMyMeshCollider.sharedMesh = mMyMesh;
    
  8. 添加射线检测及其响应方法

       void Update() {
           var ray = Camera.main.ScreenPointToRay(Input.mousePosition);
           RaycastHit hitInfo;
           Physics.Raycast(ray, out hitInfo);
           if (hitInfo.collider != null) {
               if (Input.GetMouseButtonDown(0)) {
                   Debug.Log("Click");
                   ChangeMyImage();
               }
           }
           Debug.DrawRay(ray.origin, ray.direction * 50, Color.red, 0.0f, false);
       }
    

2.2.3 关于Shader.Find的几个坑:

  • public static Shader Find( string name )

  • Shader.Find可以在脚本中动态切换着色器,而不必保留对着色器的引用。 name 是可以在任何材质的着色器弹出窗口中看到的名称,例如 “Standard”, “Unlit/Texture”, “Legacy Shaders/Diffuse” 等。

  • 如果没有引用它, Shader可能会出现找不到的情况! 因为在导出工程文件时, Unity默认情况下不会把工程里没有使用过的shader导出( unity不会分析你代码里使用了哪个内置shader. 为了防止出现”粉红色”材质, 可以采用以下几种方法之一:

    • 从场景中使用的一些Material中引用它

    • 将其添加到Edit->ProjectSettings-> Graphics->Always Included Shaders列表中

    • 将shader或其引用( 例如Material )添加到“Resources”文件夹中

2.2.4 资源下载

MyImage.unitypackage

2.3 绘制过程

利用调试来推导图形绘制过程

2.3.1 绘制过程实例

动态修改图片颜色, 通过点击按钮, 实现图片颜色的叠加, 效果如图

这里写图片描述

在自定义方法SetColor设置断点, 点击Button时, 即可触发

这里写图片描述

F11按下之后, 来到了Graphic的Color属性, 如果满足SetColor条件, 则设置顶点”dirty”

这里写图片描述

SetPropertyUtility.SetColor 方法的作用是判断是否是否需要刷新颜色, 可以防止重复绘制同一种颜色

    public static bool SetColor(ref Color currentValue, Color newValue)
    {
        if (currentValue.r == newValue.r && currentValue.g == newValue.g && currentValue.b == newValue.b && currentValue.a == newValue.a)
            return false;

        currentValue = newValue;
        return true;
    }

将当前的组件注册到Graphic的Canvas渲染队列当中

这里写图片描述

这里写图片描述

CanvasUpdateRegistry.PerformUpdate方法中使用该队列

这里写图片描述

那谁来调用PerformUpdate方法呢?

这里写图片描述

Unity手册中关于Canvas.willRenderCanvases的描述为:

  • Event that is called just before Canvas rendering happens.
  • This allows you to delay processing / updating of canvas based elements until just before they are rendered.

说明在Canvas被渲染之前, 会调用PerformUpdate方法

即调用实现了ICanvasElememt接口的类中的Rebuild方法

这里写图片描述

在此处加上断点, 然后F11,

这里写图片描述

来到了GraphicRebuild方法

这里写图片描述

UpdateGeometry更新几何体

这里写图片描述

DoMeshGeneration网格生成

这里写图片描述

OnPopulateMesh利用VertexHelper填充网格, 判断图片的类型

这里写图片描述

刷新了顶点的颜色!!!

这里写图片描述

结果出现啦!!

这里写图片描述

2.4 总结

图形绘制过程图为:

这里写图片描述

PerformUpdate注册到Canvas.willRenderCanvases中, 如果UI发生变动, 该事件触发

  • PerformUpdate事件从渲染队列中获取元素,分别调用Rebuild方法
  • Rebuild方法中完成网格, 材质等的更新

当UI发生变动时, 将需要修改的元素添加到渲染队列中, 从而实现UI的更新

猜你喜欢

转载自blog.csdn.net/jingangxin666/article/details/80143176