读书笔记15《Unity Shader入门精要》

铁名_IronName Lv4

第 16 章 Unity 中的渲染优化技术

程序优化的第一条准则 :不要优化。程序优化的第二条准则(仅针对专家 ! ) :不要优化。——Michael A.Jackson

移动平台的特点

  • GPU 架构不同。由于处理资源等条件的限制,带宽和功能更小
    为了减少overdraw(一个像素被绘制多次),PowerVR芯片(iOS设备和某些Android)使用基于瓦片的延迟渲染(Tiled-based Deferred Rendering,TBDR);Adreno(高通的芯片)和Mali(ARM的芯片)则会使用低精度的深度检测提前剔除片元;而Tegra(英伟达的芯片)使用传统架构,overdraw更可能造成性能的瓶颈。

影响性能的因素

(1)CPU

  • draw call 过多
  • 脚本或物理模拟复杂

(2)GPU

  • 顶点过多;逐顶点计算过多
  • 片元过多(分辨率过高 / overdraw);逐片元计算过多

(3)带宽

  • 纹理尺寸很大,还不压缩
  • 帧缓冲分辨率过高

优化技术

(1)CPU

  • 使用批处理技术减少draw call数目

(2)GPU

  • 减少需要处理的顶点数目
    • 优化几何体(网格)
    • 使用模型的 LOD(Level of Detail)技术
    • 使用遮挡剔除(Occlusion Culling)技术
  • 减少需要处理的片元数目
    • 控制绘制顺序
    • 警惕透明物体
    • 减少实时光照
  • 减少计算复杂度
    • 使用 Shader 的 LOD(Level of Detail) 技术
    • 代码优化

(3)带宽

  • 缩小纹理
  • 利用分辨率缩放(帧缓冲吧)

Unity 中的渲染分析工具

渲染统计窗口(Rendering Statics Window)

通过 Game 视图的右上角的菜单中 Stats 打开

  • Batches:一帧中需要进行的批处理数目
  • Saved by batching:合并的批处理数目,表明了批处理为我们节省了多少 draw call
  • Tris 和 Verts:绘制的三角面片和顶点数目
  • Screen:屏幕的大小,以及它占用的内存大小
  • SetPass:渲染使用的 Pass 的数目,每个 Pass 都需要 Unity 的 runtime 来绑定一个新的 Shader,可能造成 CPU 的瓶颈。
  • Visible skinned meshes:渲染的蒙皮网格的数目
  • Animations:播放的动画数目

性能分析器(Profiler)的渲染区域(Rendering Area)

Window -> Analysis -> Profiler

帧调试器(Frame Debugger)

其他性能分析工具

Android 平台:高通的 Adreno 分析工具;英伟达的 NVPerfHUD
iOS 平台:Unity 内置的分析器;PowerVRAM 的 PVRUniSCo shader 分析器;Xcoed 中的 OpenGL ES Driver Instruments (工具较少;宏观上的统计数据可能更有参考价值)

减少draw call数目

批处理的思想:在每次调用 draw call 时尽可能多地处理多个物体。使用统一材质的物体可以一起处理,他们之间的不同仅仅在于顶点数据的差异。

动态批处理

Unity 自动进行。使用动态批处理的物体仍然可以移动。

条件限制:

  • 能够进行动态批处理的网格的顶点属性规模要小于 900 。 例如,如果 shader 中需要使用顶点位置、法线和纹理坐标这 3 个顶点属性,那么要想让模型能够被动态批处理,它的顶点数目不能超过 300 。需要注意的是,这个数字在未来有可能会发生变化,因此不要依赖这个数据 。
  • 使用光照纹理(Lightmap) 的物体需要小心处理 。 这些物体需要额外的渲染参数,例如,在光照纹理上的索引、偏移量和缩放信息等 。因此,为了让这些物体可以被动态批处理,我们需要保证它们指向光照纹理中的同 一个位置 。
  • 多 Pass 的 shader 会中断批处理。在前向渲染中,我们有时需要使用额外的 Pass 来为模型添加更多的光照效果,但这样一来模型就不会被动态批处理了 。

静态批处理

在运行开始阶段,把需要进行静态批处理的模型合并到一个新的网格结构中。只需要进行一次合并操作,比动态批处理更加高效。

条件限制:

  • 不可以在运行时刻被移动
  • 需要占用更多的内存来存储合并后的几何结构
  • 额外的 Pass 不会被批处理。(处理平行光的部 Base Pass 部分仍然会被静态批处理)

如果在静态批处理前一些物体共享了相同的网格,那么在内存中每一个物体都会对应一个该网格的复制品,即一个网格会变成多个网格再发送给 GPU 。如果这类使用同一网格的对象很多 ,那么这就会成为一个性能瓶颈了。例如,如果在一个使用了 1 000 个相同树模型的森林中使用静态批处理,那么,就会多使用 1 000 倍的内存,这会造成严重的内存影响 。

共享材质

如果两个材质之间只有使用的纹理不同, 我们可以把这些纹理合并到一张更大的纹理中, 这张更大的纹理被称为是一张图集 (atlas)。 一旦使用了同一张纹理, 我们就可以使用同一个材质,再使用不同的采样坐标对纹理采样即可。
除了纹理不同外, 不同的物体在材质上还有一些微小的参数变化,例如,颜色不同某些浮点屈性不同。一种常用的方法就是使用网格的顶点数据(最常见的就是顶点颜色数据)来存储这些参数。

建议

  • 尽可能使用静态批处理,但小心对内存的消耗。
  • 用动态批处理注意条件限制。
  • 游戏中的小道具,例如可以捡的金币等,可以使用动态批处理。
  • 包含动画的物体,其中不动的部分可以标记为“Static”(使用静态批处理)

减少需要处理的顶点数目

优化几何体

尽可能减少模型中三角面片的数目。

模型的 LOD 技术

当对象逐渐远离摄像机时,减少模型上的面片数量,从而提高性能。
要为同一个对象准备多个包含不同细节程度的模型。

遮挡剔除技术

遮挡剔除会使用一个虚拟的摄像机来遍历场景, 从而构建个潜在可见的对象集合的层级结构。在运行时刻,每个摄像机将会使用这个数据来识别哪些物体是可见的,而哪些被其他物体挡住不可见。使用遮挡剔除技术,不仅可以减少处理的顶点数目,还可以减少overdraw, 提高游戏性能。
Unity 手册

减少需要处理的片元数目

减少overdraw

控制绘制顺序

  • 尽可能让物体从前往后绘制(使用不透明物体的渲染队列)
  • 把占据屏幕比例大的,总是在前面的物体优先绘制;天空盒最后绘制(它永远出现在所有物体后面)。

时刻警惕透明物体

几乎一定会造成 overdraw。

  • 尽量减少半透明 GUI 所占面积。实在不行就把 GUI 绘制 和 三维场景的绘制交给不同的摄像机。
  • 透明度测试的 clip/discard 会导致一些硬件的优化策略失效。

减少实时光照和阴影

  • 使用烘焙技术,把光照提前烘焙到一张光照纹理(lightmap)中。把静态物体的阴影信息存储到光照纹理中,只对动态物体使用适当的实时阴影。
  • God Ray:不是真的光源,而是通过透明纹理模拟得到的。
  • 把复杂的光照计算存储到一张查找纹理(lookup texture,LUT)中。运行时只要使用光源方向、视角方向、法线方向等参数,对LUT采样就能得到光照结果。对主要角色可以用更大分辨率的LUT,而NPC使用较小的LUT。

节省带宽

减小纹理大小

  • 用正方形纹理;纹理的长宽值是 2 的整数幂
  • 多级渐远纹理技术(mipmapping)
  • 纹理压缩。不同的 GPU 架构有自己的纹理压缩格式。Unity 可以根据不同的设备选择不同的压缩格式。(但 GUI 纹理,出于对画质的要求,有时不选择压缩)

利用分辨率缩放

雨松 MOMO - Unity3D研究院之使用Android的硬件缩放技术优化执行效率

降低计算复杂度

Shader 的 LOD 技术

1
2
3
4
SubShader {
Tags {}
LOD 200
}

可以使用 Shader.maximumLOD 或 Shader.globalMaximumLOD 来设置最大的 LOD 值。

也就是有选择地使用 Shader。。

代码优化

对象数 < 顶点数 < 像素数

  • 尽可能地把计算放在每个对象或逐顶点上。例如在实现高斯模糊和边缘检测时,把采样坐标的计算放在顶点着色器中,这样的做法远好于把它们放在片元着色器中。
  • 尽可能使用低精度的浮点值进行运算。
    • 最高精度的 float/highp 适用于存储顶点坐标等变量,计算速度最慢,尽量避免在片元着色器中使用这种精度;
    • half/mediump 适用于一些标量、纹理坐标等变量;
    • fixed/lowp 适用于绝大多数颜色变量和归一化后的方向矢量。
    • 避免对这些低精度变量进行平凡的 swizzle 操作。
    • 尽量避免在不同精度之间的转换。
  • 使用尽可能少的插值变量。如果需要对两个纹理坐标插值,通常会把它们打包在同一个 float4 变量中。但是在 PowerVR 上,不应该这么做。
  • 尽可能不使用全屏的屏幕后处理效果。尽量使用低精度运算。高精度的运算可以使用查找表(LUT),或者转移到顶点着色器中进行处理。
  • 尽量把多个特效合并到一个 Shader 中。 选择性地开启特效。
  • 尽可能不要使用分支语句和循环语句。
  • 尽可能避免使用类似 sin、tan、pow、log 等复杂的数学运算。用查找表替代。
  • 尽可能不要使用 discard 操作,因为会影响硬件的某些优化。

根据硬件条件缩放

先保证游戏最基本的配置可以在所有平台上运行良好,然后再对一些具有更高表现能力的设备,开启一些更“养眼”的效果,例如更高的分辨率,屏幕后处理特效,粒子效果等。

扩展阅读

  • Unity 手册中的移动平台优化实践指南(自己去搜)
  • Unity 手册 - 优化图像性能
  • SIGGRAPH 2011 上,Unity 进行的一个关于移动平台上 Shader 优化的演讲
  • Unite 2013 会议上,Unity 名为 针对移动平台优化 Unity 游戏的演讲
  • GDC 2014 上,Unity 展示的如何使用内置的分析器分析移动平台的游戏性能(可以去 Youtube 上找视频)
  • SIGGRAPH 2015 会议上,名为 Moving Mobile Graphics 的课程中,移动平台上 PBR 的优化技术。(可以去 Unity 的博客找)
  • 成功的移动平台游戏也是很好的学习资料。如《ShadowGun》,Unite 2011 上,该游戏的开发者给出了游戏中使用的渲染和优化技术。
  • 游戏优化实例——Unity 自带的项目《Angry Bots》。可以在 Unity 资源商店下载到完整的项目源代码。
  • 标题: 读书笔记15《Unity Shader入门精要》
  • 作者: 铁名_IronName
  • 创建于 : 2026-01-30 10:14:15
  • 更新于 : 2026-02-15 15:21:11
  • 链接: https://blog.ironname.top/2026/01/30/读书笔记15《Unity-Shader入门精要》/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论