| 维度 | 实例化 (Instancing) | 合批 (Batching) |
| 几何体要求 | 必须相同 | 可以不同 |
| 材质要求 | 必须相同 | 必须相同 |
| 单个元素变换 | 容易(修改矩阵) | 困难(修改顶点 buffer) |
| 单个元素增删 | 较容易(设置 count) | 困难(需重建) |
| Draw Call | 1 次 | 1 次 |
| 内存占用 | 低(几何体只存一份) | 高(所有顶点都存) |
| 适合场景 | 大量相同形状 | 大量不同形状的静态物体 |
| 点击检测 | 需要 GPU picking 或 instanceId | 通过 intersect.index 反查 |
3D 渲染中,CPU 每次告诉 GPU "画一个物体"就是一次 Draw Call。每次 Draw Call 都有固定开销:
- 切换材质/shader
- 绑定顶点缓冲区
- 设置 uniform 变量
- 发起绘制命令
如果场景中有 10000 个车位,每个车位一个 Mesh,就是 10000 次 Draw Call。GPU 本身很快,但 CPU 和 GPU 之间的通信成本会把帧率拖垮。
两种技术的核心目标都是:减少 Draw Call 数量。
实例化(Instancing / InstancedMesh)
原理
GPU 一次绘制命令画出同一个几何体的多个"副本",每个副本可以有不同的位置、旋转、缩放、颜色。
// Three.js 中的用法
const geometry = new THREE.BoxGeometry(2.5, 5, 0.1); // 一个车位的形状
const material = new THREE.MeshBasicMaterial();
const mesh = new THREE.InstancedMesh(geometry, material, 10000); // 10000 个实例
// 每个实例设置不同的变换矩阵
const matrix = new THREE.Matrix4();
for (let i = 0; i < 10000; i++) {
matrix.setPosition(positions[i].x, positions[i].y, positions[i].z);
mesh.setMatrixAt(i, matrix);
}
特点
- 1 次 Draw Call 画出所有实例
- 所有实例共享同一个 Geometry 和 Material
- 每个实例可以有独立的变换矩阵(位置/旋转/缩放)和颜色
- GPU 通过 gl_InstanceID 区分不同实例
适用场景
- 大量形状相同的物体:树木、草、粒子、车位标线、柱子
- 形状一样但位置/颜色不同
限制
- 所有实例必须是同一个几何体(不能一个是方形车位一个是三角形)
- 所有实例必须用同一个材质
- 单个实例的拾取(点击检测)需要额外处理
合批(Batching / Geometry Merging)
原理
把多个不同的几何体的顶点数据合并到一个大的 BufferGeometry 中,用一次 Draw Call 画出来。
// 项目中的实际做法(floor-layer-three-view.vue)
const positions: number[] = []; // 所有点的坐标放在一个数组里
const colors: number[] = [];
points.forEach(point => {
positions.push(point.x, point.y, point.z);
colors.push(r, g, b);
});
const geometry = new THREE.BufferGeometry();
geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3));
geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3));
const batchedPoints = new THREE.Points(geometry, material);
batchedPoints.userData = {
isBatched: true,
pointDataArray: metadataArray, // 保留每个点的元数据,用于点击查找
};
特点
- 1 次 Draw Call 画出所有合并后的几何体
- 不同形状的几何体可以合并(只要材质相同)
- 合并后是一个整体,单个元素的变换需要修改顶点数据
- 需要自己维护"哪个顶点属于哪个原始物体"的映射关系
适用场景
- 大量静态的不同形状物体:拓扑线段、轨迹点、点云
- 不需要频繁单独移动/删除某个元素
- 本项目中的典型用法:把同一楼层的所有 topo link 线段合并成一个 THREE.LineSegments
限制
- 合并后单个元素的变换很麻烦(需要修改 buffer 中的顶点)
- 动态增删元素需要重建整个 buffer
- 点击检测需要通过 intersect.index 反查元数据
对比
维度 实例化 (Instancing) 合批 (Batching)
几何体要求 必须相同 可以不同
材质要求 必须相同 必须相同
单个元素变换 容易(修改矩阵) 困难(修改顶点 buffer)
单个元素增删 较容易(设置 count) 困难(需重建)
Draw Call 1 次 1 次
内存占用 低(几何体只存一份) 高(所有顶点都存)
适合场景 大量相同形状 大量不同形状的静态物体
点击检测 需要 GPU picking 或 instanceId 通过 intersect.index 反查
577



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



