性能对比
同样的五千个立方体、同样的位置与朝向,分别用五千个 Mesh 与一个 InstancedMesh 绘制。 绘制调用从五千降到一次,帧率与构建耗时会实时显示。
说明
两种模式共用同一份位置、朝向与颜色数据。实例化版本把这些数据写进
一个实例矩阵缓冲与一个实例颜色缓冲;独立网格版本创建五千个
Mesh,每个都有自己的材质实例。
切换模式时观察三个数字:绘制调用、三角面、帧率。绘制调用会从个位数跳到五千以上, 三角面数量不变。这个对比能说明「对象数量」与「几何复杂度」是两个不同的瓶颈。
| 指标 | 实例化 | 独立网格 | 结论 |
|---|---|---|---|
| 绘制调用 | 1 | 5000 | 差距最大的地方 |
| 三角面 | 60000 | 60000 | 几何量相同 |
| 材质数量 | 1 | 5000 | 独立网格需要逐对象克隆材质 |
| 构建耗时 | 几十毫秒 | 数百毫秒 | 创建对象本身有成本 |
| 单对象改色 | 改实例颜色 | 直接改材质 | 实例化需要用 setColorAt |
绘制调用为什么贵
每一次绘制调用都要在 CPU 侧切换管线状态、绑定缓冲、提交命令。 单次开销是微秒级,看起来不多,但五千次就是几十毫秒,足够吃掉一帧。 把大量同形状对象合并成一次调用,是最直接有效的优化。
需要注意的是,实例化并不减少顶点与像素工作量。五千个立方体占满屏幕时, 瓶颈会转移到填充率,帧率未必有改善。它解决的是 CPU 侧的调用开销。
什么情况适合实例化
- 几何体完全一致,只有位置、旋转、缩放或颜色不同,例如树木、路灯、井盖。
- 数量在数百以上,替换为实例化能明显降低绘制调用。
- 不需要每个对象独立的材质结构(不同贴图、不同透明模式)。
如果每个对象都要不同的贴图或着色器,实例化就不适用。 此时更合适的是合并静态几何体,或按材质分批实例化。
读取统计信息
renderer.render(scene, camera);
console.log(renderer.info.render.calls); // 本帧绘制调用
console.log(renderer.info.render.triangles); // 本帧三角面
console.log(renderer.info.memory.geometries); // 常驻几何体数量
console.log(renderer.info.memory.textures); // 常驻纹理数量
info 每次渲染开始时重置,所以必须在渲染之后立刻读取。
排查显存泄漏时,观察 memory.geometries 与
memory.textures 是否持续增长,比只看内存占用更准确。
先量再优化。绘制调用、三角面、帧时间三者指向不同的问题。 不区分地「减少对象」或「减面」,经常优化了不是瓶颈的那一项, 复杂度上去了,帧率却几乎没变。