Part2:算法类
1、
常用的数据结构。
数据结构是指相互之间存在着一种或多种关系的数据元素的集合和该集合中数据元素之间的关系组成 。 常用的数据结构有:数组,栈,链表,队列,树,图,堆,散列表等。
2、
数组理解
数组是可以再内存中连续存储多个元素的结构,在内存中的分配也是连续的,数组中的元素通过数组下标进行访问,数组下标从0开始。例如下面这段代码就是将数组的第一个元素赋值为 1。 int[] data = new int[100];data[0] = 1;
优点:
a. 按照索引查询元素速度快
b. 按照索引遍历数组方便
缺点: a. 数组的大小固定后就无法扩容了
b. 数组只能存储一种类型的数据
c. 添加,删除的操作慢,因为要移动其他的元素
适用场景: 频繁查询,对存储空间要求不大,很少增加和删除的情况。
3、
List的理解
有序的对象列表,属于数据结构的一种:顺序结构。 泛型集合类,引入System.Collections.Generic命名空间,常用操作有Count属性查看长度,Add()添加,Remove()去除,AddRange()添加集合,Clear()清空集合。
4、
数组和List的核心区别
数组在C#中最早出现的。在内存中是连续存储的,所以它的索引速度非常快,而且赋值与修改元素也很简单。数组存在一些不足的地方。在数组的两个数据间插入数据是很麻烦的,而且在声明数组的时候必须指定数组的长度,数组的长度过长,会造成内存浪费,过段会造成数据溢出的错误。如果在声明数组时我们不清楚数组的长度,就会变得很麻烦。List<T>是集合,集合元素的数量可以动态变化。增加、插入、删除元素很方便。
5、
栈的理解
栈是一种特殊的线性表,仅能在线性表的一端操作,栈顶允许操作,栈底不允许操作。 栈的特点是:先进后出,或者说是后进先出,从栈顶放入元素的操作叫入栈,取出元素叫出栈。 栈的结构就像一个集装箱,越先放进去的东西越晚才能拿出来,所以,栈常应用于实现递归功能方面的场景,例如斐波那契数列。
6、
队列的理解
队列与栈一样,也是一种线性表,不同的是,队列可以在一端添加元素,在另一端取出元素,也就是:先进先出。从一端放入元素的操作称为入队,取出元素为出队。使用场景:因为队列先进先出的特点,在多线程阻塞队列管理中非常适用。
7、
链表的理解
链表是物理存储单元上非连续的、非顺序的存储结构,数据元素的逻辑顺序是通过链表的指针地址实现,每个元素包含两个结点,一个是存储元素的数据域 (内存空间),另一个是指向下一个结点地址的指针域。根据指针的指向,链表能形成不同的结构,例如单链表,双向链表,循环链表等。
优点:
a. 链表是很常用的一种数据结构,不需要初始化容量,可以任意加减元素;
b. 添加或者删除元素时只需要改变前后两个元素结点的指针域指向地址即可,所以添加,删除很快;
缺点:
a. 因为含有大量的指针域,占用空间较大;
b. 查找元素需要遍历链表来查找,非常耗时。
适用场景:
数据量较小,需要频繁增加,删除操作的场景
8、
树的理解
树是一种数据结构,它是由n(n>=1)个有限节点组成一个具有层次关系的集合。把它叫做 “树” 是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。它具有以下的特点:
a. 每个节点有零个或多个子节点;
b. 没有父节点的节点称为根节点;
c. 每一个非根节点有且只有一个父节点;
d. 除了根节点外,每个子节点可以分为多个不相交的子树;
在日常的应用中,我们讨论和用的更多的是树的其中一种结构,就是二叉树。二叉树是树的特殊一种,具有如下特点:
a. 每个结点最多有两颗子树,结点的度最大为2。
b. 左子树和右子树是有顺序的,次序不能颠倒。
c. 即使某结点只有一个子树,也要区分左右子树。
9、
散列表的理解
散列表,也叫哈希表,是根据关键码和值 (key和value) 直接进行访问的数据结构,通过key和value来映射到集合中的一个位置,这样就可以很快找到集合中的对应元素。哈希表的应用场景很多,当然也有很多问题要考虑,比如哈希冲突的问题,如果处理的不好会浪费大量的时间,导致应用崩溃。
10、
堆的理解
堆是一种比较特殊的数据结构,可以被看做一棵树的数组对象,具有以下的性质:
a. 堆中某个节点的值总是不大于或不小于其父节点的值;
b. 堆总是一棵完全二叉树;
c. 将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。常见的堆有二叉堆、斐波那契堆等。
因为堆有序的特点,一般用来做数组中的排序,称为堆排序。
11、
图的理解
图是由结点的有穷集合V和边的集合E组成。其中,为了与树形结构加以区别,在图结构中常常将结点称为顶点,边是顶点的有序偶对,若两个顶点之间存在一条边,就表示这两个顶点具有相邻关系。 按照顶点指向的方向可分为无向图和有向图: 图是一种比较复杂的数据结构,在存储数据上有着比较复杂和高效的算法,分别有邻接矩阵 、邻接表、十字链表、邻接多重表、边集数组等存储结构。
12、
二叉树的所有遍历方式的原理及优缺点
前序遍历,先访问根节点在访问左节点在访问右节点。
中序遍历,先访问左节点在访问根节点在访问右节点。
后序遍历,先访问左节点在访问右节点在访问根节点。
前中后代表的是访问根节点的时序。采用递归方式和非递归方式。前者优点是直观,编写起来简单,缺点是但其开销也比较大。非递归形式开销小,但编写复杂。
13、
设计模式相关
主要使用:工厂模式、代理模式、策略模式、观察者模式、单例模式等
a. 工厂模式:
简单工厂模式解决的问题是如何去实例化一个合适的对象。 简单工厂模式的核心思想就是:有一个专门的类来负责创建实例的过程。凡是出现了大量的产品需要创建,并且具有共同的接口时,可以通过工厂方法模式进行创建。比如说写技能是一系列类,那么就可以使用工厂模式创建。
b. 代理模式:一个是真正的你要访问的对象(目标类),一个是代理对象,真正对象与代理对象实现同一个接口,先访问代理类再访问真正要访问的对象。代理模式就是多一个代理类出来,替原对象进行一些操作,比如我们在租房子的时候回去找中介,为什么呢?因为你对该地区房屋的信息掌握的不够全面,希望找一个更熟悉的人去帮你做,此处的代理就是这个意思。再如我们有的时候打官司,我们需要请律师,因为律师在法律方面有专长,可以替我们进行操作,表达我们的想法。
代理模式的应用场景:如果已有的方法在使用的时候需要对原有的方法进行改进,此时有两种办法:
法一:修改原有的方法来适应。这样违反了“对扩展开放,对修改关闭”的原则。
法二:就是采用一个代理类调用原有的方法,且对产生的结果进行控制。这种方法就是代理模式。
c. 策略模式:定义一系列算法,并将每个算法封装起来,使他们可以相互替换,且算法的变化不会影响到使用算法的客户。策略模式的决定权在用户,系统本身提供不同算法的实现,新增或者删除算法,对各种算法做封装。因此,策略模式多用在算法决策系统中,外部用户只需要决定用哪个算法即可。
d. 观察者模式:类似于邮件订阅和RSS订阅,当我们浏览一些博客或wiki时,经常会看到RSS图标,就这的意思是,当你订阅了该文章,如果后续有更新,会及时通知你。其实,简单来讲就一句话:当一个对象变化时,其它依赖该对象的对象都会收到通知,并且随着变化,对象之间是一种一对多的关系。
e. 单例对象(Singleton)是一种常用的设计模式。在C#应用中,单例对象能保证在一个CLR中,该对象只有一个实例存在。这样的模式有几个好处:
(1)某些类创建比较频繁,对于一些大型的对象,这是一笔很大的系统开销。
(2)省去了new操作符,降低了系统内存的使用频率,减轻GC压力。
(3)有些类如交易所的核心交易引擎,控制着交易流程,如果该类可以创建多个的话,系统完全乱了。(比如一个军队出现了多个司令员同时指挥,肯定会乱成一团),所以只有使用单例模式,才能保证核心交易服务器独立控制整个流程。
14、
说出面向对象的设计原则,并分别简述它们的含义。
a. 单一职责原则 (The Single Responsiblity Principle,简称 SRP):一个类,最好只做一件事,只有一个引起它的变化。
b. 开放-封闭原则 (The Open-Close Principle,简称 OCP):对于扩展是开放的,对于更改是封闭的
c. Liskov 替换原则(The Liskov Substitution Principle,简称 LSP):子类必须能够替换其基类。
d. 依赖倒置原则 (The Dependency Inversion Pricinple, 简称 DIP):依赖于抽象。
e. 接口隔离原则 (The Interface Segregation Principle,简称 ISP):使用多个小的专门的接口,而不要使用一个大的总接口。
15、
观察者模式的深入理解?
观察者模式:一对多的关系,当被观察这发生改变时会通知所有观察者。让双方都依赖于抽象,使得各自变化不会影响另一方。
16、
MVC模式
用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。经典MVC模式中,M是指业务模型,V是指用户界面,C则是控制器,使用MVC的目的是将M和V的实现代码分离,从而使同一个程序可以使用不同的表现形式。其中,View的定义比较清晰,就是用户界面。
Part3:渲染类
1、
Unity3D Shader分哪几种,有什么区别?
a. Fixed function shader 属于固定渲染管线 Shader, 基本用于高级Shader在老显卡无法显示时的Fallback(之后有详细介绍)。使用的是ShaderLab语言,语法与微软的FX files 或者NVIDIA的 CgFX类似。固定功能管线着色器可以作为前两种着色器的备用选择,当硬件无法运行那些酷炫Shader的时,还可以通过固定功能管线着色器来绘制出一些基本的内容。
b. Vertex and Fragment Shader 最强大的Shader类型,属于可编程渲染管线. 使用的是CG/HLSL语法。顶点片段着色器可以非常灵活地实现需要的效果,但是需要编写更多的代码,并且很难与Unity的渲染管线完美集成。
c. Surface Shader Unity3d推崇的Shader类型,使用Unity预制的光照模型来进行光照运算。使用的也是CG/HLSL语法。表面着色器的抽象层次比较高,它可以轻松地以简洁方式实现复杂着色。表面着色器可同时在前向渲染及延迟渲染模式下正常工作。
三种Shader共同点:
a. 都必须从唯一一个根Shader开始
b. Properties参数部分,作用及语法完全相同
c. 具体功能都在SubShader里(Subshader: 子Shade,Shader会自上而下运行第一个硬件能支持的Subshader,主要作用是对不用硬件的支持。)
e. 都可以打标签,例如Tags { "RenderType" = "Opaque" } LOD 200 以及Lighting On等
f. 都可以Fallback
g. 都可以处理基本的功能,例如光照漫反射(Diffuse)以及镜面反射(Specular)。但是Vertex and Fragment和Surface都能实现Fixed function实现不了的高级功能,例如基于uv计算的效果等等。
三种Shader不同点:
a. Fixed function shader以及Vertex and Fragment Shader在subshader下面还有pass{},但是Surface Shader,由于已经将具体内容打包在光照模型了,不能加pass{},加了会报错。
b. Fixed function shader每句代码之后没有分号';' 但是V&F shader以及Surface shader每句代码之后都必须加分号’;
c. 核心结构不同:
Fixed function shader的核心是Material{} 以及 SetTexture[_MainTex]{}。
Vertex and Fragment Shader的核心是
CGPROGRAM
#
pragma
vertex vert
#
pragma
fragment frag
#include “UnityCG.cginc"
ENDCG
Surface Shader的核心是:
(1) 自带光照模型Lambert,也不做顶点处理,那么只需要一个表面处理函数surf即可;
CGPROGRAM
#
pragma
surface surf Lambert
ENDCG
(2) 这套表示使用的是自己写的光照模型AkLightModel,并且使用了顶点处理函数vert;
CGPROGRAM
// surface 表面处理函数 光照模型函数 顶点处理:函数
#
pragma
surface surf AkLightModel vertex:vert
//执行顺序 顶点处理函数 -> 表面处理函数 -> 光照模型函数 ->颜色值
2、
什么是渲染管道?
渲染管道是指在显示器上为了显示出图像而经过的一系列必要操作。 渲染管道中的很多步骤,都要将几何物体从一个坐标系中变换到另一个坐标系中去。主要步骤有:本地坐标->视图坐标->背面裁剪->光照->裁剪->投影->视图变换->光栅化。
3、
GPU的工作原理。
GPU全称是Graphic Processing Unit--图形处理器,其最大的作用就是进行各种绘制计算机图形所需的运算,包括顶点设置、光影、像素操作等。其关键操作:
a. 顶点处理:这阶段GPU读取描述3D图形外观的顶点数据并根据顶点数据确定3D图形的形状及位置关系,建立起3D图形的骨架。在支持DX8和DX9规格的GPU中,这些工作由硬件实现的Vertex Shader(定点着色器)完成。
b. 光栅化计算:显示器实际显示的图像是由像素组成的,我们需要将上面生成的图形上的点和线通过一定的算法转换到相应的像素点。把一个矢量图形转换为一系列像素点的过程就称为光栅化。例如,一条数学表示的斜线段,最终被转化成阶梯状的连续像素点。
c. 纹理帖图:顶点单元生成的多边形只构成了3D物体的轮廓,而纹理映射(texture mapping)工作完成对多变形表面的帖图,通俗的说,就是将多边形的表面贴上相应的图片,从而生成“真实”的图形。TMU(Texture mapping unit)即是用来完成此项工作。
d. 像素处理:这阶段(在对每个像素进行光栅化处理期间)GPU完成对像素的计算和处理,从而确定每个像素的最终属性。在支持DX8和DX9规格的GPU中,这些工作由硬件实现的Pixel Shader(像素着色器)完成。
e. 最终输出:由ROP(光栅化引擎)最终完成像素的输出,1帧渲染完毕后,被送到显存帧缓冲区。 总结:GPU的工作通俗的来说就是完成3D图形的生成,将图形映射到相应的像素点上,对每个像素进行计算确定最终颜色并完成输出。
4、
两种阴影判断的方法、工作原理。 本影和半影:
本影:景物表面上那些没有被光源直接照射的区域(全黑的轮廓分明的区域)。
半影:景物表面上那些被某些特定光源直接照射但并非被所有特定光源直接照射的区域(半明半暗区域)。
工作原理:从光源处向物体的所有可见面投射光线,将这些面投影到场景中得到投影面,再将这些投影面与场景中的其他平面求交得出阴影多边形,保存这些阴影多边形信息,然后再按视点位置对场景进行相应处理得到所要求的视图(利用空间换时间,每次只需依据视点位置进行一次阴影计算即可,省去了一次消隐过程)
5、
Alpha Blend工作原理。
Alpha混合通常是为了实现透明效果,不过只能针对某块区域进行alpha操作,透明度可设,透明效果的程度通过alpha值来控制,对于一个8bit的alpha chanel,可以产生0-255级透明效果,0表示完全透明,255表示完全不透明。
6、
写出光照计算中的diffuse的计算公式。
diffuse = Kd x colorLight x max(N*L,0);Kd 漫反射系数、colorLight 光的颜色、N 单位法线向量、L 由点指向光源的单位向量、其中N与L点乘,如果结果小于等于0,则漫反射为0。
7、
LOD是什么,优缺点是什么?
LOD(Level of detail)多层次细节,是最常用的游戏优化技术。它按照模型的位置和重要程度决定物体渲染的资源分配,降低非重要物体的面数和细节度,从而获得高效率的渲染运算。
8、
什么是LightMap?
LightMap:就是指在三维软件里实现打好光,然后渲染把场景各表面的光照输出到贴图上,最后又通过引擎贴到场景上,这样就使物体有了光照的感觉。
9、
Vertex Shader是什么,怎么计算?
顶点着色器是一段执行在GPU上的程序,用来取代Fixed Pipeline中的Transformation和Lighting,Vertex Shader主要操作顶点。Vertex Shader对输入顶点完成了从Local Space到Homogeneous Space(齐次空间)的变换过程,Homogeneous Space,即Projection Space的下一个Space。在这其间共有World Transformation, View Transformation和Projection Transformation及Lighting几个过程。
10、
MipMap是什么,其作用是?
MipMapping:在三维计算机图形的贴图渲染中有常用的技术,为加快渲染进度和减少图像锯齿,贴图被处理成由一系列被预先计算和优化过的图片组成的文件,这样的贴图被称为MipMap。
11、
什么叫动态合批?跟静态合批有什么区别?
如果动态物体共用着相同的材质,那么Unity会自动对这些物体进行批处理。动态批处理操作是自动完成的,并不需要你进行额外的操作。
区别:动态批处理一切都是自动的,不需要做任何操作,而且物体是可以移动的,但是限制很多。静态批处理:自由度很高,限制很少,缺点可能会占用更多的内存,而且经过静态批处理后的所有物体都不可以再移动了。
12、
请问Alpha Test在何时使用?能达到什么效果?
Alpha Test:中文就是透明度测试。简而言之就是V&F shader中最后fragment函数输出的该点颜色值(即上一讲frag的输出half4)的alpha值与固定值进行比较。Alpha Test语句通常于Pass{}中的起始位置。Alpha Test产生的效果也很极端,要么完全透明,即看不到,要么完全不透明。在Unity中alpha值的来源主要有三种,颜色混合方程:
其中OP在这里定义为混合操作(共有五种形式)
a. ADD表示相加操作
b. SUBTRACT表示相关(目标-源)
c. REV_SUBTRACT表示反射的相关(源-目标)
d. MIN表示取源、目标颜色中较小值
e. MAX表示取源、目标颜色中较大值
公式:实际显示颜色 = 前景颜色
Alpha/255 + 背景颜色
(255-Alpha)/255
13、
有A和B两组物体,有什么办法能够保证A组物体永远比B组物体先渲染?
把A组物体的渲染对列大于B物体的渲染队列。
14、
什么是DrawCall?DrawCall高了又什么影响?如何降低DrawCall?
Unity中,每次引擎准备数据并通知GPU的过程称为一次Draw Call。DrawCall越高对显卡的消耗就越大。降低DrawCall的方法:
a. Dynamic Batching
b. Static Batching
c. 高级特性Shader降级为统一的低级特性的Shader
15、
实时点光源的优缺点是什么?
可以有Cookies – 带有Alpha通道的立方图(Cubemap )纹理。点光源是最耗费资源的。
16、
Unity的Shader中,Blend SrcAlpha OneMinusSrcAlpha这句话是什么意思?
作用就是Alpha混合。公式:最终颜色 = 源颜色
源透明值 + 目标颜色
(1 - 源透明值)
17、
简述水面倒影的渲染原理。
原理就是对水面的贴图纹理进行扰动,以产生波光玲玲的效果。用Shader可以通过GPU在像素级别作扰动,效果细腻,需要的顶点少,速度快。
16、
部分渲染名词解析。
a. alpha:在颜色红、绿、兰(R、G、B)之后添加的第4个颜色值,用于提供对象颜色的透明度。当它的值为0.0时,就意味着完全透明,为1.0时表示不透明。
b. 环境光 (Ambient light):场景中的光线,它并非来自特定的点源和方向。外围光说明所有曲面都很平坦,而且位于所有的边上。
c. 反走样(Anti-aliasing):用于平滑直线、曲线和多边形边的渲染方法。这种技巧将均衡紧邻直线的像素颜色。对于直线上的像素以及紧邻直线的那些像素,它具有柔和的过渡的可视效果,这样,就提供了更平滑的外观。
d. 纵横比(Aspect ratio):窗口宽度与高度的比率。特别是用窗口的宽度(以像素为单位)除以像素的高度(以像素为单位)。
e. 贝塞尔(Bezier)曲线:一条曲线,它的形状是由曲线的控制点定义的,而不是由一组型值点定义的,曲线的性质由伯恩斯坦(Bernstein)基函数决定。
f. 样条(Spline):通用术语,用于描述由曲线的控制点创建的曲线。在曲线的形状上具有拉伸效果。当沿着曲线的长度在各个点上加一点压力时,将与易弯曲材料的反作用力相似。
g. B样条曲线:一种自由曲线,它的形状是由曲线的控制点和节点矢量定义的,曲线的性质由B样条基函数决定。
17、
写光照计算中的diffuse的计算公式。
a. 实际光照强度 I = 环境光(Iambient) + 漫反射光(Idiffuse) + 镜面高光(Ispecular)
b. 环境光:Iambient= Aintensity* Acolor; (Aintensity表示环境光强度,Acolor表示环境光颜色)
c. 漫反射光:Idiffuse = Dintensity
Dcolor
N.L;(Dintensity表示漫反射强度,Dcolor表示漫反射光颜色,N为该点的法向量,L为光源向量)
d. 镜面反射光:Ispecular = Sintensity
Scolor
(R.V)^n;(Sintensity表示镜面光照强度,Scolor表示镜面光颜色,R为光的反射向量,V为观察者向量,n称为镜面光指数) diffuse = Kd
colorLight
max(N * L, 0)
18、
Alpha Test 和Alpha Blending区别。
Alpha Test:采用一种很霸道极端的机制,只要一个像素的Alpha不满足条件,那么它就会被Fragment Shader舍弃,被舍弃的Fragments不会对后面的各种Tests产生影响;否则,就会按正常方式写入到缓存中,并进行正常的深度检验等等,也就是说,Alpha Test是不需要关闭ZWrite的。Alpha Test产生的效果也很极端,要么完全透明,即看不到,要么完全不透明。
Alpha Blending:则是一种中庸的方式,它使用当前Fragment的Alpha作为混合因子,来混合之前写入到缓存中颜色值。但Alpha Blending麻烦的一点就是它需要关闭ZWrite,并且要十分小心物体的渲染顺序。如果不关闭ZWrite,那么在进行深度检测的时候,它背后的物体本来是可以透过它被我们看到的,但由于深度检测时大于它的深度就被剔除了,从而我们就看不到它后面的物体了。因此,我们需要保证物体的渲染顺序是从后往前,并且关闭该半透明对象的ZWrite。
19、
简述四元数的作用,四元数对欧拉角的优点?
四元数用于表示旋转
a. 四元数一般定义如下:q=w+xi+yj+zk 其中 w,x,y,z 是实数。同时,有: i
i=-1 j
j=-1 k*k=-1。
b. 四元数也可以表示为:q=[w,v] 有多种方式可表示旋转,如 axis/angle、欧拉角(Euler angles)、矩阵(matrix)、四元组等。 相对于其它方法,四元组有其本身的优点:
(1) 四元数不会有欧拉角存在的 gimbal lock 问题[万向节死锁];
(2) 四元数由 4 个数组成,旋转矩阵需要 9 个数;
(3) 两个四元数之间更容易插值;
(4) 四元数、矩阵在多次运算后会积攒误差,需要分别对其做规范化(normalize)和正交化 (orthogonalize),对四元数规范化更容易;
(5) 与旋转矩阵类似,两个四元组相乘可表示两次旋转
20、
向量的点乘、叉乘以及归一化的意义?
a. 点乘描述了两个向量的相似程度,结果越大两向量越相似,还可表示投影;
b. 叉乘得到的向量垂直于原来的两个向量;
c. 标准化向量:只用在关系方向,不关心大小的方面。
21、
矩阵相乘的意义及注意点。
用于表示线性变换:旋转、缩放、投影、平移、仿射。注意矩阵的蠕变:误差的积累
22、
怎么判断两个平面是否相交?不能用碰撞体,说出计算方法。
对于两个平面Ax+By+Cz+D=0与ax+by+cz+d=0,只要(A,B,C)与(a,b,c)不成比例,这两个平面就是相交的。
23、
法线贴图 、CG 动画。
a. 法线贴图:是在原物体的凹凸表面的每个点上均作法线,通过 RGB 颜色通道来标记法线的方向, 你可以把它理解成与原凹凸表面平行的另一个不同的表面,但实际上它又只是一个光滑的平面。
b. CG动画:原为 Computer Graphics 的英文缩写。随着以计算机为主要工具进行视觉设计和生产的一系列相关产业的形成,国际上习惯将利用计算机技术进行视觉设计和生产的领域通称为 CG。它既包括技术也包括艺术,几乎囊括了当今电脑时代中所有的视觉艺术创作活动,如平面印刷品的设计、网页设计、三维动画、影视特效、多媒体技术、以计算机辅助设计为主的建筑设计及工业造型设计等。
24、
什么是局部坐标,什么是世界坐标?
世界坐标是不会变的,一直以世界坐标轴的 XYZ 为标准。 局部坐标其实就是自身的坐标,会随着物体的旋转而变化的。
25、
什么是矢量图
矢量图:计算机中显示的图形一般可以分为两大类——矢量图和位图。矢量图使用直线和曲线来描述图形,这些图形的元素是一些点、线、矩形、多边形、圆和弧线等等,它们都是通过数学公式计算获得的。例如一幅花的矢量图形实际上是由线段形成外框轮廓, 由外框的颜色以及外框所封闭的颜色决定花显示出的颜色。由于矢量图形可通过公式计算获得,所以矢量图形文件体积一般较小。矢量图形最大的优点是无论放大、缩小或旋转等不会失真;最大的缺点是难以表现色彩层次丰富的逼真图像效果。
26、
四元组是什么?
所谓四元数,就是把 4 个实数组合起来的东西。4 个元素中,一个是实部,其余 3 个是虚部。
27、
什么是投影矩阵 ?
投影矩阵是一个典型的缩放和透视矩阵。投影变换将视锥变换成一个直平行六面体的形状。因为视锥的近处比远处小,这样就会对靠近摄像机的对象起到放大的作用,也就将透视应用到了场景当中
28、
什么是UV?
UV坐标是指所有的图象文件都是二维的一个平面。水平方向是U,垂直方向是V,通过这个平面的,二维的UV坐标系。我们可以定位图象上的任意一个象素。
29、
什么是顶点程序和片段程序?
顶点着色器是一组指令代码,这组指令代码在顶点被渲染时执行。片段着色器也是在 GPU 上运行的小程序。它们负责输出每个呈现的三角形像素的最终像素颜色。基本而言,它的工作原理如下:片段着色器以输入的形式收到顶点着色器通过管道传递的所有这些片段。
30、
如何实现以下人物在树丛中部分透明效果?
Shader
"Custom/PlayerDiffuse"
{
Properties
{
_NotVisibleColor
(
"NotVisibleColor (RGB) "
,
Color
)
=
(
0
.
3
,
0
.
3
,
0
.
3
,
1
)
_MainTex
(
"Base (RGB) "
,
2D
)
=
"white"
{
}
}
SubShader
{
Tags
{
"Queue"
=
"Geometry+500"
"RenderType
"="
Opaque"
}
LOD
200
Pass
{
ZTest Greater
Lighting Off
ZWrite Off
//Color [_NotVisibleColor]
Blend SrcAlpha OneMinusSrcAlpha
SetTexture
[
_MainTex
]
{
ConstantColor
[
_NotVisibleColor
]
combine constant
*
texture
}
}
Pass
{
ZTest
LEqual
Material
{
Diffuse
(
1
,
1
,
1
,
1
)
Ambient
(
1
,
1
,
1
,
1
)
}
Lighting Off
SetTexture
[
_MainTex
]
{
combine texture
}
}
}
FallBack
"Diffuse"
}
*
*
31
、
*
*
游戏中要怎么实现矩阵相乘?
//矩阵相乘
public
static
float
[
]
[
]
Mul
(
float
[
]
[
]
a
,
float
[
]
[
]
b
)
{
//确保矩阵a的列数和b的行数相等
if
(
a
[
0
]
.
长度
!=
b
.
长度
)
{
return
null
;
}
//用来存放结果的矩阵,axb的结果为a的行数和b的列数
float
[
]
[
]
result
=
new
float
[
a
.
长度
]
[
b
[
0
]
.
长度
]
;
//对a的每行进行遍历
for
(
int
i
=
0
;
i
<
a
.
长度
;
i
++
)
{
//对b的每列进行遍历
for
(
int
j
=
0
;
j
<
b
[
0
]
.
长度
;
j
++
)
{
//c为每一个点的值
float
c
=
0
;
//第i行j列的值为a的第i行上的n个数和b的第j列上的n个数对应相乘之和,其中n为a的列数,也是b的行数,a的列数和b的行数相等
for
(
int
k
=
0
;
k
<
a
[
0
]
.
长度
;
k
++
)
{
c
+=
(
a
[
i
]
[
k
]
*
b
[
k
]
[
j
]
)
;
}
result
[
i
]
[
j
]
=
c
;
}
}
return
result
;
}
Part3:优化类
1、
如何优化内存?
有很多种方式:
a. 压缩自带类库;
b. 将暂时不用的以后还需要使用的物体隐藏起来而不是直接Destroy掉;
c. 释放AssetBundle占用的资源;
d. 降低模型的片面数,降低模型的骨骼数量,降低贴图的大小;
e. 使用光照贴图,使用多层次细节(LOD),使用着色器(Shader),使用预设(Prefab)。
2、
下列代码在运行中会产生几个临时对象?
string
a
=
new
string
(
"abc"
)
;
a
=
(
a
.
ToUpper
(
)
+
"123"
)
.
Substring
(
0
,
2
)
;
3、
数组在C#中初始化第一行是会出错的(Java中倒是可行)。 应该这样初始化:string b = new string(new char[]{'a','b','c'});
4、
下列代码在运行中会发生什么问题?如何避免?
List
<
int
>
ls
=
new
List
<
int
>
(
new
int
[
]
{
1
,
2
,
3
,
4
,
5
}
)
;
foreach
(
int
item
in
ls
)
{
Console
.
WriteLine
(
item
*
item
)
;
ls
.
Remove
(
item
)
;
}
会产生运行时错误,因为foreach是只读的。不能一边遍历一边修改。
5、
如何在Unity3D中查看场景的面试,顶点数和Draw Call数?如何降低Draw Call数?
答:在Game视图右上角点击Stats。降低Draw Call 的技术是Draw Call Batching。
6、
Unity在移动设备上的一些优化资源的方法
(1) 使用assetbundle,实现资源分离和共享,将内存控制到200m之内,同时也可以实现资源的在线更新;
(2) 顶点数对渲染无论是CPU还是GPU都是压力最大的贡献者,降低顶点数到8万以下,fps稳定到了30帧左右;
(3) 只使用一盏动态光,不使用阴影,不使用光照探头;
(4) 粒子系统是CPU上的占比比较重,剪裁粒子系统,合并同时出现的粒子系统,自己实现轻量级的粒子系统;
(5) 尽量减少Smooth Group;
(6) 去掉法线计算;
(7) Animator本身比较耗性能,Animator出视野不更新,删除无意义的Animator,把不需要跟骨骼动画和动作过渡的地方全部使用。
(8) 除主角外都不要跟骨骼运动Apply Root Motion;
(9) 绝对禁止掉那些不带刚体带包围盒的物体(static collider )运动;
(10) 每帧递归的计算Finalalpha改为只有初始化和变动时计算;
(11) 不要每帧计算Viewsize 和Windowsize;
(12) 代码剪裁:使用Strip Level ,使用.net2.0 Subset;
(13) 与美术商定一个严格的经过科学验证的美术标准,并在Unity编辑器里面配以检查资源工具。
7、
纹理加载进内存以后占用内存如何计算?
a. 纹理Texture加载进内存后,大小计算公式如下:纹理内存大小(字节) = 纹理宽度 x 纹理高度 x 像素字节。
b. 像素字节 = 像素通道数(R/G/B/A) x 通道大小(1字节/半字节)举例:比如一个1024
1204的RGBA 32bit的纹理占用多大内存?1024
1024
(4
8bit)/8 byte。
8、
UGUI常用优化技巧?
(1) 设置图集可以合批,ProjectSetting>Editor>SpritePacker>Always Enabled (Legacy Sprite Packer),创建一个SpriteAtlas图集,设置Objects For Packing,将合批的文件拖入,合批的文件中的Sprite中Packing Tag属性设置成SpriteAtlas名称;
(2) 避免不同图集或材质的倾轧OverLay,Scene视图调节为Writeframe模式可以更容易查看;
(3) 选择OverLay时,在Hierarchy视图中排序错误,导致合批的步骤增加,注意Hierarchy中顺序,层级关系;
(4) Mask会增加一个Draw,并且Mask里面的图片不会和外面的图片合批;
(5) Text和RichText区别,富文本会造成三角面增加,版本问题,2019版本不会增加;
(6) 空的Image造成一个Drawcall并且会打断合批;
(7) 颜色渐变和可以用通过脚本控制material材质,本质是更改Tint属性,这样既能满足颜色渐变,又能避免网格频繁重建造通过 ImageColor 来改变材质球属性,最后达到不重构Mesh的效果;切换元素的贴图时也一样可以做到不重构的效果,由于贴图更换会导致重构,为了达到不重构的目的可以给一个自定义材质球的并且更换材质球中的贴图。观察Profiler中,Hierarchy面板下,搜索SendWillRenderCanvases和BuildBatch两个函数:
Canvas.SendWillRenderCanvases():主要是计算Canvas下面所有的顶点数据
Canvas.BuildBatch():则是负责进行Mesh绘制与合批。
(8) 接受射线脚本:Empty4Raycast,使用的此脚本可以在有效接受点击事件的情况下,不增加drawcall和打断合批
public
class
Empty4Raycast
:
MaskableGraphic
{
protected
Empty4Raycast
(
)
{
useLegacyMeshGeneration
=
false
;
}
//当一个UI元素生成顶点数据时会调用OnPopulateMesh(VertexHelper vh)函数,
//我们可以在这个函数中修改顶点的数据或者获取顶点的数据,这里是清空,不增加drawcall。
protected
override
void
OnPopulateMesh
(
VertexHelper
toFill
)
{
toFill
.
Clear
(
)
;
}
}
(9) 减少OverDraw脚本:PolygonImage
OverDraw就是指GPU对屏幕一片区域的重复绘制次数;慎用Mask组件,它自带两层OverDraw;慎用Text组件的OutLine和Shadow,Shadow会增加一层OverDraw,而OutLine是复制了四份Shadow实现的;不使用空白或透明的Image,尽管alpha = 0,还是会渲染并增加一层OverDraw可以使用Text Mesh Pro性能更好,功能强大,并且免费。
(10) 关闭不需要的Raycast Target选项(Image、Text、RawImage),降低每帧检测射线的性能消耗。解决RaycastTarget勾选过多的问题,使用DebugUILine脚本检测,会出现蓝色线框表示勾选了射线检测。
static
Vector3
[
]
fourCorners
=
new
Vector3
[
4
]
;
void
OnDrawGizmos
(
)
{
foreach
(
MaskableGraphic
g
in
GameObject
.
FindObjectsOfType
<
MaskableGraphic
>
(
)
)
{
if
(
g
.
raycastTarget
)
{
RectTransform
rectTransform
=
g
.
transform
as
RectTransform
;
rectTransform
.
GetWorldCorners
(
fourCorners
)
;
Gizmos
.
color
=
Color
.
blue
;
for
(
int
i
=
0
;
i
<
4
;
I
++
)
Gizmos
.
DrawLine
(
fourCorners
[
i
]
,
fourCorners
[
(
i
+
1
)
%
4
]
)
;
}
}
}
(11) 用多个Canvas将屏幕划分出不同的区域,降低网格重建带来的性能损耗;
(12) 避免或者降低赋值的操作,降低网格重建频率 例如在Update中执行textComponent.text = “菜鸟小听歌”,如果数值没有变化可以不用赋值,有变化一秒赋值一次;
(13) Canvas避免使用Blocking Objects来遮挡射线,因为这是一个相当消耗性能的操作;
(14) Screen Space 和 World Space 中的Camera必须要指定,负责会每帧7-10次调用Object.FindObjectWithTag;
(15) 频繁需要SetActive的物体可以使用Canvas Group组件,可以有效降低重建消耗,禁用Canvas组件本身可以避免重建消耗来达到隐藏的效果(前提节点没有改变),但是对应脚本上的OnEnable、OnDisable会触发,所以可以使用Canva Group组件达到效果;
(16) 降低使用字体的种类。字体Font资源需要单独打包,否则会造成字体被重复打包到对应的UI Bundle里面;
(17) Mask 与 Rect Mask 2D 的区别
a. Rect Mask 2D
b.节省 DrawCall 和 Overdraw
c. 增加 Cull 开销(Canvas.SendWillRenderCanvases())
d. 持续开销较低
e. 拖动时开销较高
(18) UI动静分离,降低drawcall动态合批。原因:元素移动就会导致Mesh重绘,将动态和静态分离,让合并范围缩小,方法:UGUI使用Canvas节点作为划分,将动态UI元素放入专门Mesh合并的Canvas节点上,将静态UI元素放入专门Mesh合并Canvas节点:动态Canvas、静态Canvas;
(19) UI预加载:在特殊的空闲时间点预加载资源,不集中在某一个时间点;
(20) UI字体拆分:字体单独打包,常用字拆出来,单独生成一个字体文件;
(21) UI滚屏优化。
本文详细探讨了Unity3D内存优化技巧,包括内存压缩、临时对象管理、纹理内存计算,以及如何降低DrawCall、优化UGUI、移动设备优化等方面的方法。此外,还介绍了如何查看和减少DrawCall,以及在游戏开发中的具体优化实践。
4365

被折叠的 条评论
为什么被折叠?



