元素码农
基础
UML建模
数据结构
算法
设计模式
网络
TCP/IP协议
HTTPS安全机制
WebSocket实时通信
数据库
sqlite
postgresql
clickhouse
后端
rust
go
java
php
mysql
redis
mongodb
etcd
nats
zincsearch
前端
浏览器
javascript
typescript
vue3
react
游戏
unity
unreal
C++
C#
Lua
App
android
ios
flutter
react-native
安全
Web安全
测试
软件测试
自动化测试 - Playwright
人工智能
Python
langChain
langGraph
运维
linux
docker
工具
git
svn
🌞
🌙
目录
▶
Unity脚本执行机制
▶
执行流程
主线程与游戏循环
事件函数执行顺序
脚本编译管线
▶
运行时环境
Mono与IL2CPP对比
垃圾回收机制
值类型与引用类型内存布局
▶
渲染管线剖析
▶
架构设计
SRP核心架构
BatchRendererGroup原理
GPU Instancing实现
▶
优化策略
动态合批与静态合批
剔除优化原理
LOD系统实现
▶
物理引擎原理
▶
核心架构
PhysX集成原理
碰撞检测算法
关节系统实现
▶
性能优化
空间划分策略
多线程物理模拟
固定时间步长原理
▶
内存管理体系
▶
内存分配
Native内存管理
托管堆扩展机制
内存碎片处理
▶
资源生命周期
AssetBundle卸载策略
对象池实现原理
资源引用追踪
发布时间:
2025-03-23 09:01
↑
☰
# Unity动态合批与静态合批原理 本文将深入探讨Unity的动态合批(Dynamic Batching)和静态合批(Static Batching)技术原理。通过本文,你将了解这两种批处理技术的工作机制、性能特点和最佳实践,以及如何在项目中合理使用它们来优化渲染性能。 ## 基础概念 ### 批处理技术简介 批处理是一种重要的渲染优化技术,它可以: 1. 减少Draw Call 2. 降低CPU开销 3. 提高渲染效率 4. 优化内存使用 ### 两种批处理方式 ```csharp // 动态合批示例 public class DynamicBatchingExample : MonoBehaviour { void Start() { // 动态合批自动进行 // 无需特殊设置 } } // 静态合批示例 public class StaticBatchingExample : MonoBehaviour { void Start() { // 标记为Static gameObject.isStatic = true; // 或者通过StaticBatchingUtility var objects = GameObject.FindGameObjectsWithTag("Batchable"); StaticBatchingUtility.Combine(objects, gameObject); } } ``` 主要区别: 1. 动态合批 - 运行时自动合并 - 适用于移动物体 - 无需预处理 - 有严格限制 2. 静态合批 - 预处理合并 - 适用于静态物体 - 需要额外内存 - 限制较少 ## 动态合批原理 ### 工作机制 ```csharp // 动态合批管理器 public class DynamicBatchManager { private struct BatchableObject { public Mesh mesh; public Material material; public Matrix4x4 transform; public int vertexCount; } private void ProcessDynamicBatch( List<BatchableObject> objects) { // 1. 检查是否可以合批 if (!CanBeDynamicallyBatched(objects)) return; // 2. 合并网格数据 var combinedMesh = new Mesh(); var vertices = new List<Vector3>(); var normals = new List<Vector3>(); var uvs = new List<Vector2>(); foreach (var obj in objects) { // 3. 转换顶点到世界空间 var worldVertices = new Vector3[obj.mesh.vertexCount]; for (int i = 0; i < obj.mesh.vertexCount; i++) { worldVertices[i] = obj.transform.MultiplyPoint3x4( obj.mesh.vertices[i]); } // 4. 添加到合并网格 vertices.AddRange(worldVertices); normals.AddRange(obj.mesh.normals); uvs.AddRange(obj.mesh.uv); } // 5. 设置合并后的网格数据 combinedMesh.SetVertices(vertices); combinedMesh.SetNormals(normals); combinedMesh.SetUVs(0, uvs); } private bool CanBeDynamicallyBatched( List<BatchableObject> objects) { // 检查合批条件 foreach (var obj in objects) { // 1. 顶点数限制 if (obj.vertexCount > 300) return false; // 2. 材质属性 if (obj.material.enableInstancing) return false; // 3. 渲染状态 if (obj.material.passCount > 1) return false; } return true; } } ``` 关键步骤: 1. 条件检查 - 顶点数限制 - 材质要求 - 渲染状态 2. 数据合并 - 顶点转换 - 属性合并 - 索引重建 3. 渲染优化 - 批次管理 - 状态排序 - 缓存利用 ### 限制条件 ```csharp // 动态合批限制检查 public class BatchingLimitations { private bool CheckBatchingLimitations(GameObject obj) { var renderer = obj.GetComponent<MeshRenderer>(); var mesh = obj.GetComponent<MeshFilter>().mesh; // 1. 网格限制 if (mesh.vertexCount > 300) return false; // 2. 材质限制 if (renderer.sharedMaterial.shader.disableBatching) return false; // 3. 变换限制 var transform = obj.transform; if (transform.localScale != Vector3.one) return false; // 4. 渲染状态 if (renderer.receiveShadows != otherRenderer.receiveShadows) return false; return true; } } ``` 主要限制: 1. 网格要求 - 顶点数≤300 - 相同网格 - 相同拓扑 2. 材质要求 - 相同材质 - 相同着色器 - 相同属性 3. 变换限制 - 统一缩放 - 无奇异变换 - 连续变换 ## 静态合批原理 ### 工作机制 ```csharp // 静态合批处理器 public class StaticBatchProcessor { private struct BatchGroup { public List<MeshRenderer> renderers; public Material material; public int vertexCount; public int triangleCount; } private void ProcessStaticBatch( List<MeshRenderer> renderers) { // 1. 分组管理 var batchGroups = new Dictionary<Material, BatchGroup>(); foreach (var renderer in renderers) { var material = renderer.sharedMaterial; if (!batchGroups.ContainsKey(material)) { batchGroups[material] = new BatchGroup { renderers = new List<MeshRenderer>(), material = material }; } batchGroups[material].renderers.Add(renderer); } // 2. 处理每个批次组 foreach (var group in batchGroups.Values) { CombineStaticBatch(group); } } private void CombineStaticBatch(BatchGroup group) { // 1. 准备合并数据 var combines = new List<CombineInstance>(); foreach (var renderer in group.renderers) { var mesh = renderer.GetComponent<MeshFilter>().mesh; combines.Add(new CombineInstance { mesh = mesh, transform = renderer.transform.localToWorldMatrix }); } // 2. 创建合并网格 var combinedMesh = new Mesh(); combinedMesh.CombineMeshes(combines.ToArray()); // 3. 创建批处理对象 var batchObject = new GameObject("StaticBatch"); var filter = batchObject.AddComponent<MeshFilter>(); var renderer = batchObject.AddComponent<MeshRenderer>(); filter.mesh = combinedMesh; renderer.material = group.material; // 4. 禁用原始对象 foreach (var r in group.renderers) { r.enabled = false; } } } ``` 处理流程: 1. 预处理 - 对象分组 - 数据收集 - 资源准备 2. 网格合并 - 顶点合并 - UV重映射 - 法线重建 3. 优化处理 - 内存优化 - 渲染优化 - 资源管理 ### 内存影响 ```csharp // 内存管理示例 public class MemoryManagement { private class MemoryAnalyzer { public struct MemoryStats { public int vertexCount; public int indexCount; public int materialCount; public long totalMemory; } public MemoryStats AnalyzeBatchMemory( GameObject[] objects) { var stats = new MemoryStats(); // 1. 计算原始内存 foreach (var obj in objects) { var mesh = obj.GetComponent<MeshFilter>().mesh; stats.vertexCount += mesh.vertexCount; stats.indexCount += mesh.triangles.Length; } // 2. 估算合批后内存 stats.totalMemory = CalculateBatchedMemory( stats.vertexCount, stats.indexCount); return stats; } private long CalculateBatchedMemory( int vertexCount, int indexCount) { // 顶点数据 long vertexMemory = vertexCount * ( sizeof(float) * 3 + // 位置 sizeof(float) * 3 + // 法线 sizeof(float) * 2 // UV ); // 索引数据 long indexMemory = indexCount * sizeof(int); return vertexMemory + indexMemory; } } } ``` 内存特点: 1. 内存开销 - 额外顶点数据 - 变换矩阵 - 材质副本 2. 内存管理 - 资源复用 - 内存池化 - 垃圾回收 ## 最佳实践 ### 选择策略 ```csharp // 批处理策略选择 public class BatchingStrategy { private enum BatchingType { None, Dynamic, Static, GPUInstancing } private BatchingType DetermineBatchingType( GameObject obj) { var renderer = obj.GetComponent<MeshRenderer>(); var mesh = obj.GetComponent<MeshFilter>().mesh; // 1. 检查是否适合动态合批 if (IsSuitableForDynamicBatching(obj)) return BatchingType.Dynamic; // 2. 检查是否适合静态合批 if (IsSuitableForStaticBatching(obj)) return BatchingType.Static; // 3. 检查是否适合GPU Instancing if (IsSuitableForGPUInstancing(obj)) return BatchingType.GPUInstancing; return BatchingType.None; } private bool IsSuitableForDynamicBatching( GameObject obj) { // 检查动态合批条件 var mesh = obj.GetComponent<MeshFilter>().mesh; return mesh.vertexCount <= 300 && !obj.isStatic && obj.transform.localScale == Vector3.one; } private bool IsSuitableForStaticBatching( GameObject obj) { // 检查静态合批条件 return obj.isStatic && obj.GetComponent<MeshRenderer>() != null; } private bool IsSuitableForGPUInstancing( GameObject obj) { // 检查GPU Instancing条件 var renderer = obj.GetComponent<MeshRenderer>(); return renderer != null && renderer.material.enableInstancing; } } ``` 选择建议: 1. 使用动态合批当: - 对象会移动 - 顶点数较少 - 材质相同 2. 使用静态合批当: - 对象不移动 - 共享材质 - 内存充足