🎮 Unity GPU 渲染优化完全手册:从 DrawCall 到 Shader 的性能飞跃
💡 GPU 优化的价值 :
DrawCall 居高不下,帧率提不上来?
OverDraw 太严重,GPU 压力过大?
想优化 Shader,却不知从何入手?
移动平台优化有哪些特殊注意事项?
这篇文章! 将系统讲解 Unity GPU 优化技术,从渲染管线到着色器,让画面更流畅!
一、GPU 性能瓶颈分析 1.1 GPU 瓶颈类型
瓶颈类型
特征
检测方法
像素填充受限
分辨率高、OverDraw 严重
Frame Debugger / RenderDoc
顶点处理受限
顶点数多、复杂几何体
减少顶点数测试
带宽受限
纹理采样多、高分辨率纹理
降低纹理分辨率测试
算力受限
复杂着色器计算
简化 Shader 测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ┌─────────────────────────────────────────────────────────────────────────┐ │ GPU 渲染管线瓶颈定位 │ ├─────────────────────────────────────────────────────────────────────────┤ │ │ │ 应用阶段 几何阶段 光栅化阶段 像素处理阶段 │ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │ │ CPU │──────>│ 顶点着色 │──────>│ 裁剪/ │─────>│ 像素着色 │ │ │ │ 准备数据 │ │ 器 │ │ 透视除法│ │ 器 │ │ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ │ │ │ │ │ │ │ ▼ ▼ ▼ ▼ │ │ Draw Calls 顶点数量 图元数量 像素填充率 │ │ 合批优化 LOD 优化 裁剪优化 OverDown 优化 │ │ │ └─────────────────────────────────────────────────────────────────────────┘
1.2 性能分析工具
工具
平台
用途
Unity Profiler
全平台
整体性能分析
Frame Debugger
Editor
单帧渲染分析
RenderDoc
Desktop
详细 GPU 分析
Xcode Instruments
iOS
GPU Driver / Tiler
Snapdragon Profiler
Android
Adreno GPU 分析
Mali Graphics Debugger
Android
Mali GPU 分析
二、DrawCall 优化 2.1 DrawCall 原理 1 2 3 4 5 6 7 单个 DrawCall 的开销: ┌─────────────┬─────────────┬─────────────┬─────────────┐ │ CPU 准备 │ 数据传输 │ GPU 状态 │ 渲染执行 │ │ 渲染状态 │ 到 GPU │ 切换 │ │ └─────────────┴─────────────┴─────────────┴─────────────┘ ↑ ↑ ↑ [固定开销] [带宽开销] [状态切换开销]
2.2 合批技术
技术
原理
适用场景
限制
Static Batch
运行前合并静态几何体
不移动的场景物体
内存增加
Dynamic Batch
运行时合并小网格
<900顶点的动态物体
顶点数限制
GPU Instancing
单次绘制多实例
相同材质的重复物体
需要 Shader 支持
SRP Batcher
按Shader变体分组
可编程渲染管线
Shader 兼容性
2.3 Static Batching 1 2 3 4 5 6 7 8 gameObject.isStatic = true ;
优点
缺点
大幅减少 DC
内存增加 (合并多份网格)
运行时无开销
不适合动态物体
跨平台兼容
合并后无法单独移动
2.4 Dynamic Batching 1 2 3 4 5 6 7 8 9 PlayerSettings.SetMobileMTRendering(BuildTargetGroup.Android, false ); PlayerSettings.SetMobileMTRendering(BuildTargetGroup.iOS, false );
2.5 GPU Instancing 1 2 3 4 5 6 7 8 9 10 #pragma multi_compile_instancing MeshRenderer renderer = GetComponent<MeshRenderer>(); renderer.enableInstancing = true ; Matrix4x4[] matrices = new Matrix4x4[instancesCount]; Graphics.DrawMeshInstanced(mesh, 0 , material, matrices);
💡 提示 :GPU Instancing 是移动端的首选合批方式!
三、OverDraw 优化 3.1 OverDraw 问题 1 2 3 4 5 6 7 8 9 10 11 OverDraw 示意图: 相机视野 │ ▼ ┌─────────────────────┐ │ ╔═══╗ ╔═══╗ │ 每个方块被绘制多次 │ ║ A ║ ║ B ║ │ A(背景) -> B(中景) -> C(前景) │ ╚═══╝ ╚═══╝ │ OverDraw = 3x │ ╔══════╗ │ │ ║ C ║ │ └────┴───────┴────────┘
3.2 OverDraw 检测 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 Shader "Custom/OverDraw" { SubShader { Tags { "Queue" ="Transparent+1" } Pass { Blend One One ZWrite Off ZTest Always CGPROGRAM #pragma vertex vert #pragma fragment frag struct appdata { float4 vertex : POSITION; }; struct v2f { float4 vertex : SV_POSITION; }; v2f vert (appdata v ) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); return o; } fixed4 frag (v2f i ) : SV_Target { return fixed4(0.1 , 0.1 , 0.1 , 0 ); } ENDCG } } }
3.3 OverDraw 优化策略
策略
说明
效果
渲染顺序
不透明物体从前向后
减少 Early-Z 剔除失败
剔除背面
剔除相机看不见的背面
减少填充率
遮挡剔除
不渲染被遮挡物体
大幅减少 OverDraw
透明合并
合并透明 UI 元素
减少 UI OverDraw
四、LOD (Level of Detail) 4.1 LOD 原理 1 2 3 4 5 6 7 8 9 10 11 12 距离 / LOD 级别: ┌─────────────────────────────────────────┐ │ │ │ Far LOD2 (低模) ◄─────────────────┐ │ │ 30m │ │ │ Mid LOD1 (中模) ◄──────────┐ │ │ │ 15m │ │ │ │ Near LOD0 (高模) ◄────┐ │ │ │ │ 5m │ │ │ │ │ ▼ ▼ ▼ │ │ Camera │ └─────────────────────────────────────────┘
4.2 LOD Group 使用 1 2 3 4 5 6 7 8 9 10 11 LODGroup lodGroup = gameObject.AddComponent<LODGroup>(); LOD[] lods = new LOD[3 ]; lods[0 ] = new LOD(0.5f , new Renderer[] { highPolyRenderer }); lods[1 ] = new LOD(0.2f , new Renderer[] { midPolyRenderer }); lods[2 ] = new LOD(0.01f , new Renderer[] { lowPolyRenderer }); lodGroup.SetLODs(lods); lodGroup.RecalculateBounds();
4.3 LOD 切换距离建议
平台
LOD 数量
切换距离
PC/Console
3-4 级
较远
Mobile High
3 级
中等
Mobile Low
2-3 级
较近
五、遮挡剔除 (Occlusion Culling) 5.1 遮挡剔除原理 1 2 3 4 5 6 7 8 9 10 11 12 13 无遮挡剔除: Camera │ ├──> 渲染 A (可见) ├──> 渲染 B (被 A 挡住但仍渲染) └──> 渲染 C (被 A 挡住但仍渲染) 有遮挡剔除: Camera │ ├──> 渲染 A (可见) ├──> 跳过 B (被遮挡) └──> 跳过 C (被遮挡)
5.2 遮挡剔除配置 1 2 3 4 5 6 7 8 GameObject.GetComponent<OcclusionPortal>().open = true ;
5.3 Occlusion Portal 1 2 3 4 OcclusionPortal portal = gameObject.AddComponent<OcclusionPortal>(); portal.open = false ; portal.open = true ;
六、Shader 优化 6.1 着色器复杂度优化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 fixed4 frag (v2f i ) : SV_Target { fixed4 col = tex2D(_MainTex, i.uv); col.rgb *= sin(_Time.y * 10 ) * pow(col.r, 2.0 ); return col; } fixed4 frag (v2f i ) : SV_Target { fixed4 col = tex2D(_MainTex, i.uv); col.rgb *= _ColorMultiplier.rgb; return col; }
6.2 移动平台 Shader 注意事项
问题
优化方案
pow/exp/log
使用查找表
sin/cos
使用近似函数
纹理采样
使用 Mipmaps
精度
mobile 使用 half/fixed
分支
尽量避免动态分支
6.3 精度优化 1 2 3 4 5 6 7 8 9 // ✅ 移动平台使用低精度 // Vertex Shader float4 pos : POSITION; // 需要高精度 half3 normal : NORMAL; // 法线用 half float2 uv : TEXCOORD0; // UV 用 float // Fragment Shader fixed4 col : SV_Target; // 颜色用 fixed half3 viewDir : TEXCOORD0; // 方向用 half
七、纹理优化 7.1 纹理格式
平台
推荐格式
说明
iOS
ASTC 4x4 / PVRTC
硬件支持
Android
ETC2 / ASTC
兼容性考虑
PC
DXT / BC7
压缩率高
7.2 纹理尺寸 1 2 3 4 5 6 7 PlayerSettings.SetTextureStreamingBudgetPerScene(512 * 1024 * 1024 ); TextureImporter importer = AssetImporter.GetAtPath(path) as TextureImporter; importer.streamingMipmapsActive = true ; importer.streamingMipmapsAddAllCameras = true ;
7.3 纹理图集 1 2 3 4 5 6 7 8 9 10 11 12 图集优势: ┌─────────────────────────────────────┐ │ 单张纹理 纹理图集 │ │ ┌───┐ ┌───┐ ┌───┐ ┌───────┐ │ │ │ A │ │ B │ │ C │ │ A B C │ │ │ └───┘ └───┘ └───┘ │ D E F │ │ │ ┌───┐ ┌───┐ ┌───┐ │ G H I │ │ │ │ D │ │ E │ │ F │ └───────┘ │ │ └───┘ └───┘ └───┘ │ │ │ │ 6 DrawCalls 1 DrawCall │ └─────────────────────────────────────┘
八、光照优化 8.1 实时光照 vs 烘焙光照
类型
性能开销
质量
用途
实时点光
高
高
动态光源
实时聚光灯
很高
高
手电筒等
烘焙光照
低
高
静态场景
Light Probes
中
中
动态物体
LPPV
中
中
大场景动态物体
8.2 光照优化策略 1 2 3 4 5 6 7 8 9 Light light = GetComponent<Light>(); light.renderMode = LightRenderMode.ForcePixel; light.renderMode = LightRenderMode.ForceVertex; light.shadows = LightShadows.None;
九、粒子系统优化 9.1 粒子优化策略
优化项
说明
减少粒子数
使用最大粒子数限制
使用 Prewarm
预热避免启动开销
禁用不必要的模块
Collision / Trails 等
使用 Sprite 粒子
比 Mesh 粒子快
降低更新频率
使用 Fixed Timestep
9.2 粒子配置示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 ParticleSystem ps = GetComponent<ParticleSystem>(); var main = ps.main;main.maxParticles = 100 ; main.prewarm = true ; main.simulationSpace = ParticleSystemSimulationSpace.Local; main.scalingMode = ParticleSystemScalingMode.Local; var collision = ps.collision;collision.enabled = false ; var renderer = ps.GetComponent<ParticleSystemRenderer>();renderer.renderMode = ParticleSystemRenderMode.Stretch; renderer.alignment = ParticleSystemRenderSpace.View;
十、UI 渲染优化 10.1 UGUI 优化要点
优化项
说明
Canvas 分割
不同频率更新的 UI 分离
隐藏空 Text
空 Text 会占用大量内存
避免 Layout
减少 Layout Group 嵌套
使用 Sprite Atlas
合并 UI 图集
禁用 Raycast Target
不需要交互的元素
10.2 Canvas 设置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 Canvas canvas = GetComponent<Canvas>(); canvas.renderMode = RenderMode.ScreenSpaceOverlay; canvas.renderMode = RenderMode.ScreenSpaceCamera; canvas.worldCamera = uiCamera; Canvas additionalCanvas = uiCanvas.gameObject.AddComponent<Canvas>(); additionalCanvas.overrideSorting = true ; additionalCanvas.sortingOrder = 100 ;
十一、移动平台特殊优化 11.1 移动 GPU 特性
GPU
特点
优化建议
Adreno
TBDR 架构
控制 OverDraw
Mali
TBDR 架构
减少带宽压力
PowerVR
TBDR 架构
利用 HSR
Tegra
IMR 架构
关注像素复杂度
💡 TBDR (Tile-Based Deferred Rendering):移动 GPU 主流架构,对 OverDraw 敏感。
11.2 移动平台检查清单
十二、优化总结 12.1 快速检查清单
类别
检查项
目标
DrawCall
减少 DC
<100 (Mobile)
OverDraw
控制填充率
<2x 平均
Shader
简化复杂度
移动端 <50 指令
纹理
压缩格式
全部压缩
光照
烘焙为主
<2 实时光
粒子
限制数量
<500 同屏
12.2 性能目标
平台
目标帧率
GPU 时间预算
PC
60/144 FPS
<16ms / <6ms
Console
30/60 FPS
<33ms / <16ms
Mobile High
60 FPS
<16ms
Mobile Mid
30 FPS
<33ms
Mobile Low
30 FPS
<33ms
十三、参考资料
转载请注明来源 ,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 1487842110@qq.com