[Unity3D]Unity中影响渲染顺序的因素总结

一,Camera Depth

相机组件上设置的相机深度,深度越大越靠后渲染。

二,透明,不透明物体分隔

RenderQueue 2500是透明与不透明的分水岭。
同一个相机下
Renderqueue小于2500的物体始终在Renderqueue大于2500之前绘制。

三,Sorting Layer

在Tags&Layers设置中可见
如果Camera相同,那接下来就看Sorting Layers,越低越早绘制。
Sorting Layers是通过Renderer的SortingLayerName属性设置的。
全局的Soring Layers在Edit->ProjectSettings->Tags&Layers中设置可以通过代码设置render.sortingLayerName="Name"运行时设置物体的sortinglayer,如果在粒子系统中看,可以看到Render项的Sorting Layers下拉表单中会列出所有的Sorting Layers的名字。

四,Order In Layer

相对于Sorting Layer的子排序,用这个值做比较时只有Sorting Layer在同一层的时候才有效。
order是设置一个数字,数字越大,越显示在最上面。

五,RenderQueue

shader中对Tags设置的“Queue”。
如果上面几项都相同的情况下,就要设置RenderQueue了,renderQueue是Material的一个属性,其实就是shader中的renderQueue,这个值是一个int属性,数值越小,越靠近前面渲染。

六,深度排序。按照包围盒的深度进行排序

不透明物体由近到远排序优先
透明物体由远到进排序优先
按照包围盒的中心点的深度进行排序。

深度补间

ZBias,当两个包围盒中心点深度值相同或者很近的物体在一起时,排序有可能出错所以对于这个点的z值可以进行微调。

上图

[unity3D]assetbundle卸载方案

前言

关于ab包的卸载,最重要的一部分就是实例化以后的gameobject还依赖这个ab包的资源导致ab包卸载以后gameobject会出现资源丢失,但是如果不被卸载,内存就会持续增加。现阶段有两个方案1:引用计数,被引用了几次;2:弱引用,销毁gameobject内存回收,引用自动消除。

assetbundle卸载原理

当场景中的object被移除后,unity不会自动卸载相关的asset,所以asset的卸载需要我们自己来调用。Asset卸载清理可以是特定时刻卸载,也能手动强制卸载。

调用方法为Assetbundle.Unload(bool)和Resource.UnLoadUnusedAssets这两个方法。

AssetBundle.Unload(true)

卸载实例化的Asset,如果其中的某些asset还在被object所引用,就会出现丢失情况

通过这种方法卸载,资源会被卸载的很彻底,完全从内存移除即使还在代码中引用,也会被移除。

AssetBundle.UnLoad(false)

切断ab与其asset之间的关联,不会卸载asset,所以引用不会丢失,asset还在内存中,但是切断例联系,所以再次从ab包中实例化asset的时候不会返回已经初始化过的asset,而是重新实例化,就会出现asset的冗余。

Resource.UnloadUnusedAssets

卸载不被引用的asset,如果asset被gameobject引用,不会卸载。

被场景引用,不会被卸载

被代码引用,不会被卸载。

调用的消耗很大,该函数的主要作用是查找并卸载不再使用的资源。

Asset的引用计数卸载方案

主要是当加载asset的资源的时候,加载一次计数+1,卸载一次计数-1,当减到不用当时候就可以unloadAsset了。

在做UI当资源管理当时候,因为当前所用的资源都是这个window窗口的。所以可以用一个资源栈来做。当打开一个窗口当时候,做一个标志位,在这个标志位以上的资源都是这个窗口所使用加载的。当关闭窗口的时候,把这个阶段的资源全部卸载掉。使用栈就需要窗口的打开和关闭是一一对应的。

Asset的弱引用卸载方案

关键词WeakReference。这个类的作用就是可以在GC的时候释放对象,回收内存。当我们把Asset做弱引用,那当产生一次GC的时候,我们就可以调用Resource.UnloadUnusedAssets来回收Asset。这个方法的危险点就是在于调用Resource.UnloadUnusedAssets的时候消耗很大,所以调用的时机得把握好。AssetBundle.UnLoad(false)也可以,但是释放对象得走一次GC,c#的GC自动调用的时间不知道,手动调用的话会使内存一下增加很多。

[unity3D]关于设置物体父节点和设置物体显示

最近在做系统的gameobject池管理,所以需要研究一下物体不可见,和设置parent的最优搭配

物体不可见方案

1:可移动物体位置到摄像机可照射范围之外;2:设置物体大小到0;3:禁用component组件;4:禁用gameobject;5:设置layer层级为摄像机不可见层级

测试数据
2D界面设置大小
2D界面设置layer层级

测试下来发现禁用layer为最优化的方案。

此测试为3D摄像机的测试数据,只测试了cpu计算耗时,此耗时为unity内部实现方法的耗时,即函数耗时。

缩小scale在3D摄像机里是不会减少DC的但是在2D下,是可以减少DC的。猜测的区别为:在2D摄像机下,会重组mesh,当scale为0的时候会被剔除出mesh渲染中,这个为2D的实现方法,后续整理这块数据。在3D摄像机下的时候,不管scale是否为0,这个物体的矩阵还是存在的,所以还是要进行矩阵转换和渲染。

在2D下移走坐标是不会减少DC的,但是加一个2Dmask就可以。猜测为:UI和2Dmask的矩阵求交集,超出就不渲染。

后续补充:

在设置layer的时候,你如果设置物体父节点的layer但是子节点是不管用的,所以你只能循环子节点来设置,在这个时候产生了循环遍历,消耗就变成指数级别的上升。最优选择为设置大小。还产生了GC。

设置parent方案

设置parent为null,和设置parent为具体的gameobject;Setparent第二个参数worldPositionStays为true和false,当这个值设置成false的时候是不会修改localposition的坐标,这个就相当于,直接把物体的世界坐标给修改了。

测试结果为设置parent不能为null,为null是最耗时的, worldPositionStays可设置为false,这样最优的,设置成true的时候设置parent的耗时和设置parent的层级有关系,如果层级最深的话耗时最长

方案选择

根据以上的测试结果,方案为设置物体的parent为最浅根节点worldPositionStays设置为false,以设置layer的显示层级来决定是否渲染。

拓展发散

当需要隐藏一个物体不被摄像机渲染的时候,我们可以选择设置layer。还有一个测试数据为当获取物体的localposition的时候比获取物体的position要快将近一倍。所以unity保存物体的坐标系是否为localpostion和一个世界坐标的转换矩阵,然后position为使用的时候才去计算。还有设置parent为null的时候慢了超过1/9,设置为null的时候是否重新生成了一份场景物体树的根节点。这些都需要以后好好研究一下。最后一句,不考虑数量级的优化都是假的。

工作问题

涉及的专业知识: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