[UGUI]ugui中同一batch的遮挡顺序

前言

最近被人问到同一batch中ugui的前后顺序是怎么表现出来的,之前我只考虑过一帧是怎么前后顺序的渲染,所以这次我讲说一下一个batch中的图片渲染顺序

知识点

  1. 使用xcode的FPS截帧
  2. 渲染的点的坐标
  3. 渲染三角形的顺序
  4. 渲染三角形的uv顺序

正文

渲染图片的本质

在一般情况下,我们常说的渲染一张图片其实是记录下一张图片的四个顶点位置,通过mvp变换,从模型空间转换到屏幕空间,然后渲染到显示设备上。在渲染的时候,我们需要定义三角形的顺序,根据对应渲染设备所使用的坐标系,我们通过左手或者右手定则,来确定渲染的朝向。定义的uv坐标来确定这三个点所需要渲染的颜色在uv图上的什么位置。通过上面的概念我们就应该能知道渲染一个图所需要简单的数据结构了。

我们只考虑最上面的那张图我们来简单定义一下数据结构

vector3[]index   --点坐标
int[2*3] vertex     --三角形的渲染顺序
int[2*3] UV             --uv坐标id

通过上面的结构体,我们就能正常的渲染出一张图片出来的。

ugui合批本质

我这里所说的合批是ugui的动态合批。一般来说,你可以认为它就是问了把多个mesh合并成一个mesh传到gpu去渲染,那什么是mesh呢?其实就是我们上面所说的那个结构体。既然知道了它是什么,我们就好来理解mesh的合并了。首先在ugui我们会对渲染的物体进行一次排序(这个内容我已经拖更很久了,大家可以去看官方的一节课)然后通过这个排序结果,我们就可以开始记录合并的mesh内容了。如果是同一个材质,同一个贴图,并且是相邻的,那就记录一次合并,如果不可以,那就把上一次合并的结果callback到gpu做一次渲染,剩下的再进行合并记录。

同一批次的遮挡顺序

通过上面我们就可以知道,同一个批次里面,如果图片前后有遮挡关系;首先我们通过排序,记录下所有点的位置信息,然后通过顺序我们记录下来三角形的渲染顺序

在这张图片,我们就能看出来,其实在这个批次的渲染里,我们是记录下来所有点,和所有三角形的信息给gpu去渲染的,在这次合批中我们是不会进行三角形的剔除操作。就算是所有做了一次完全的遮挡,其实我们上传的数据也是做了一次全量的记录的。所以同一batch的遮挡就是在合批时候的排序顺序的关系。

总结

渲染的本质是通过三角形的排布来做纹理的展示,所有的优化方式都是围绕这三角形怎么减少呀,三角形的颜色该怎么计算出来,这样的方式来做优化的。了解本质,你才能更轻松的理解方案。

工作问题

涉及的专业知识:1:编程语言;2:unity基础框架;3:渲染知识。

编程语言:

问题1:

class entrusttest
    {
        delegate void hello();
        static List<hello> a = new List<hello>();
        public entrusttest()
        {
            for (int i = 0; i < 10; i++)
            {
                a.Add(delegate
                {
                    Console.WriteLine(i);
                });
            }
            for(int i = 0; i < a.Count; i++)
            {
                a[i]();
            }
        }
    }

问题为打印出来的值为什么。答案为:全部都是10;
根据大佬的说法:这个为闭包概念,i会变成全局的唯一变量。
学习重点:lamda表达式
在c#基础上看到过这个概念,但是实际应用就没用过。

问题2

class entrusttest1
    {
        delegate void hello();
        public entrusttest1()
        {
            abc();
        }
        public void abc()
        {
            hello a;
            a = b;
            a += c;
            a += d;
            a();
        }
        public void b()
        {
            Console.WriteLine("a");
        }
        public void c()
        {
            Console.WriteLine("c");
        }
        public static void d()
        {
            Console.WriteLine("dddd");
        }
    }

这里委托+=是否可以为静态函数
答案是:可以
拓展:在构造函数里不能直接执行委托属性。必须包一层方法。构造函数和普通函数有什么区别呢?如果都是线性执行的话那有啥区别。
学习重点:函数。

问题3

class box
    {
        long a = 1000000000L;
        public box()
        {
            object c = a;
            int d = (int)c;
        }
    }

这里是否会抛出异常。
答案是:会,装箱的类型和拆箱的类型不匹配
学习重点:拆装箱,值类型和引用类型
拓展:值类型在装箱的时候怎么把栈上的内存放到堆上。内存变化。

问题4

soket的实现
答案:
学习重点:
拓展:

问题5

hashtable是怎么存内容的,又能存对象,也能存基础类型
答案:存对象的时候是保存对象的指针,keys是把指针再存到一个数组里(未验证)
学习重点:数据结构
拓展:

问题6

UI系统框架改怎么实现
答案:根据大佬理解,应该是做一个UI栈,先进后出。
学习重点:数据结构知识,设计模式
拓展:

问题7

找到字符串中第一个出现两次的字符
答案:

string str = "dafdfureqwnfahfgi";
        Hashtable c;
        public stringtext()
        {
            c = new Hashtable();
            char[] b = str.ToCharArray();
            for(int i = 0; i < b.Length; i++)
            {
                if (c.Contains(Convert.ToInt32( b[i])))
                {
                    Console.WriteLine(b[i].ToString());
                    return;
                }
                else
                {
                    c.Add(Convert.ToInt32(b[i]), Convert.ToInt32(b[i]));
                }
            }

        }

大佬给了一个新的解答方案,用二进制做匹配,保存进制位,如果位一样,就相当于前面有相等的值:

 class stringtext1
    {
        int n = 0;
        string str = "dfdafaffasrwer";
        public stringtext1()
        {
            char[] b = str.ToCharArray();
            for (int i = 0; i < b.Length; i++)
            {
                int offset = Convert.ToInt32(b[i])-96;
                int c = 1 << offset;
                n = c^n;
                int r = n & c;
                if (r == 0)
                {
                    Console.WriteLine(b[i].ToString());
                    return;
                }
            }
        }

    }

学习重点:字符比较转换
拓展:是否还有效率更高的解答

问题8

c#的GC是怎么实现的

答案:

学习重点

拓展:引用类型的回收,值类型的回收

问题9

lua的GC实现原理

答案:

学习重点:

拓展:

问题10

lua元表的概念

答案:

学习重点:

拓展:

问题11

事件,委托有什么不同

答案:

学习重点:

拓展

unity框架知识

问题1

unity的渲染顺序
答案:先camera depth的值,越小越优先;sorting layer层,越小越优先;sorting order 越小越优先;renderqueue越小越优先;z轴大小;创建顺序。
学习重点:渲染顺序。
拓展:renderqueue的值2500为分界线,renderqueue的值相等,大2500则远到近,小于则近到远。个属性分别实在哪些组件上。

问题2

unity的遮挡剔除
答案:
学习重点:遮挡剔除
拓展:剔除算法放在哪一步做。

问题3

unity怎么在label上加描边
答案:
学习重点:渲染label的方法
拓展:在哪步做渲染加描边最优

问题4

unity的协程是怎么实现暂停后继续执行当前行
答案:协程实现了一个迭代器
学习重点:协程实现
拓展:C#的迭代器

问题5

一个物体被渲染到看到,经历里几次坐标转换
答案:物体坐标到世界坐标,世界坐标到摄像机坐标,摄像机坐标到裁剪空间,裁剪坐标到屏幕空间,屏幕空间到视口空间。
学习重点:坐标转换
拓展:坐标转换计算

问题6

unity系统方法调用顺序
答案:Awake>OnEnable>Start>FixedUpdate>Update>LateUpdate>OnGUI>OnDestroy
学习重点:各种函数的调用时机
拓展:系统全部函数调用顺序和时机

问题7

recttransform的介绍

答案:

学习重点

拓展

问题8

scrollview加3D特效

答案:

学习重点

拓展

问题9

聊天的图文混搭

答案:

学习重点

拓展

渲染

问题1

光栅化做了什么
答案:
学习重点:
拓展:

问题2