Unity3D游戏开发笔记-2【优化】

  1. 渲染流程

    1. 首先接收用户提供的几何数据,并且将他们输入到一系列着色器阶段中进行处理,最后将数据转换到最终渲染的图像。顶点数据->顶点着色器->细分控制着色器->细分计算着色器->几何着色器->图元设置->剪切剔除->光栅化->片元着色器->最终渲染的图像。

    2. 不一定会用到所有的着色阶段,只有顶点着色器和片元着色器是必需的。一个复杂的引用程序可能包含许多个顶点着色器,但在同一时刻只能有一个顶点着色器起作用。

    3. 每一帧画面的渲染过程,大致如下
      i. CPU检查场景中的每个对象来确定他是否需要渲染,并且将每个需要渲染的对象加入到DrawCall指令中,部分具有相同属性的对象可能会Batch到同一个DrawCall中。
      ii. CPU会向GPU发送SetPassCall指令,在当前RenderState与下次不同时,SetPassCall会告知GPU在下次渲染Mesh时要使用哪种配置。
      iii. GPU根据CPU的指令执行任务,执行顺序与指令发送顺序相同。
      iv. DrawCall->GPU渲染Mesh,SetPassCall->GPU更新RenderState

  2. 渲染优化

    1. 填充率
      i. 降低游戏分辨率
      ii. 使用光照贴图并减少逐像素灯光的数量
      iii. 减少FragmentShader中复杂或非必要的计算,不追求高精度的部分计算移至VertexShader中完成
      iv. 减少粒子及透明物体的叠加或混合(降低OverDraw)
      v. 减少后期效果的数量或优化其算法降低计算量。
    2. 显存带宽
      i. 纹理的数量
      ii. 纹理的尺寸
      iii. 根据平台芯片选择对应的纹理压缩格式和修改项目设置中的TextureQuality。
      iv. 会发生远近变化或缩放的物体需要使用Mipmap来减少带宽占用并提升表现效果,减轻摩尔纹或改善近距离分辨率不足的情况,其他情况下则可以关闭Mipmap来节省内存。
      v. 关闭纹理的ReadWriteEnable,减少内存和带宽占用。
    3. 顶点数
      i. 降低模型复杂度,可使用法线纹理提高表现效果。
      ii. 调整模型导入属性,选择相应的压缩等级。
      iii. 在项目设置中调整顶点数据的压缩内容。
      iv. 使用LOD调整显示细节。
      v. 使用OcclusionCulling,进行遮挡剔除。
    4. DrawCall
      i. 静态批处理,会增加网格的资源量,占用内存及磁盘空间。
      ii. 动态批处理,在游戏运行时进行Batching操作,需要注意的是,动态批处理的网格顶点属性要少于900个,所以在shader中使用到的顶点属性要尽可能的少,并且要避免使用多通道shader。
      iii. 美术方面的预处理,根据设定的可视范围,调整合并材质及网格,能够使用Tiling的纹理尽可能采用Tilling的方式。
      iv. 使用LOD调整显示细节。
      v. 使用遮挡剔除,但部分情况下可能会造成额外开销。
    5. 减少反射,阴影,深度图的影响范围及调整相机的裁剪范围,会明显降低渲染开销。
  3. 脚本优化

    1. 移除脚本中的无效代码和空方法。
    2. 频繁使用的变量可以声明为全局变量。
    3. 减少游戏运行时获取组件的操作,应尽可能在全局变量中缓存。
    4. 能够在资源制作过程中序列化的,优先考虑通过序列化的方法获得组件。
    5. 客户端的可见组件,可以通过检测OnBecameVisible/OnBecameInvisible来确认是否在可见范围内,并且据此调整逻辑更新频率或内容。
    6. 频繁显示关闭的组件对象,可以通过移出屏幕的方式降低开销。
    7. 减少Object的实例化和销毁操作,尽可能使用对象池重复利用。
    8. 需要在Update中频繁查找的同一组件,尽可能的在初始化阶段缓存起来。
    9. 字符串拼接,最好使用StringBuilder。
    10. 减少高频的复杂计算,如实时性需求不高,可做分帧处理。
  4. 物理碰撞

    1. 调整Fixed Timestep,控制物理计算频率。
    2. 减少MeshCollider,优先使用BoxCollider,SphereCollider。
    3. 网络同步操作若需要使用物理组件,应打开IsKinematic,并采用相应的运动公式控制移动,检测方式为OnTrigger。
  5. GC

    1. Mono运行时的托管堆,主要是类实例,字符串和数组。
    2. 触发方式
      i. 堆内存不足时,自动调用GC
      ii. 手动调用GC
    3. 内存分配
      i. 首先检查空闲内存是否足够。
      ii. 如果不够,进行一次GC,回收内存。
      iii. 如果仍然不够,会向操作系统申请内存,扩充现有堆内存。
      iv. 已分配的内存不会交还给操作系统,只会回收到Mono的堆内存。
    4. 内存回收过程
      i. 停止需要分配内存的线程。
      ii. 遍历内存,找到无引用的内存,并标记。
      iii. 回收被标记的内存到空闲内存。
      iv. 继续启动被停止的线程。
    5. 内存泄漏
      i. 引用丢失,导致无法通过任何途径访问到,但该对象仍有引用关系,无法被标记成垃圾。
      ii. 大部分情况都是由于静态对象引用导致的,静态对象中不再使用的对象应将其设置为null,使其可以被GC标记并回收。
    6. 注意点
      i. 字符串连接的处理,是在堆上分配的空间,结束后原字符串会成为垃圾,新字符串为新分配的空间。
      ii. 尽可能不要使用foreach,优先使用for
      iii. 使用对象池,重复使用对象,减少实例化销毁的操作。
      iv. 尽量不要使用LINQ。
      v. 优先使用局部变量。
      vi. 需要频繁new的,优先使用结构体代替类。

猜你喜欢

转载自blog.csdn.net/ss5214423/article/details/96136274