Three.js中如何通过单次绘制调用渲染多个蒙皮网格?
蒙皮网格单次绘制调用的实现方案
原生InstancedMesh仅支持无蒙皮的普通网格,不兼容需要骨骼动画计算的SkinnedMesh,目前有两种成熟的可行方案可以实现蒙皮网格的单次绘制调用:
方案1:扩展GPU蒙皮的实例化支持
- 核心逻辑:保留原始模型的蒙皮权重、蒙皮索引属性,额外新增实例化骨骼矩阵属性,在GPU侧完成每个实例的独立蒙皮计算
- 实现步骤:
- 给原几何体添加实例化属性
instanceBoneMatrices,每个实例对应的数据长度为「骨骼总数 × 16」(单根骨骼的世界矩阵为4×4共16个浮点数),按需求设置最大实例数 - 通过材质的
onBeforeCompile钩子修改着色器逻辑,将原本从全局uniform读取的骨骼矩阵,替换为从实例化属性中读取当前实例对应的骨骼矩阵,其余蒙皮计算逻辑保持不变 - 动画更新阶段,单独计算每个实例的当前帧骨骼世界矩阵,写入
instanceBoneMatrices对应的缓冲区,标记needsUpdate = true即可
- 给原几何体添加实例化属性
- 优势:完全GPU侧计算,无额外CPU蒙皮开销,支持运行时动画混叠、IK等动态骨骼效果,性能最优,适合实例量大于100的场景
- 限制:实例化属性的存储上限受WebGL顶点属性数量限制,单模型骨骼数量超过64时会占用较多显存
方案2:预烘焙动画帧+普通InstancedMesh实现
- 核心逻辑:如果使用的是固定序列的循环动画,不需要运行时修改骨骼状态,可以提前将动画的顶点帧烘焙为纹理,规避蒙皮逻辑直接用普通
InstancedMesh实现实例化 - 实现步骤:
- 离线/运行时预烘焙动画:遍历动画的所有关键帧,计算每帧下所有顶点的世界位置,将数据存储为2D纹理,纹理的一行对应一帧的所有顶点数据
- 给普通
InstancedMesh的几何体新增实例化属性instanceAnimFrame,存储每个实例当前播放的帧序号 - 自定义材质着色器,在顶点着色器阶段根据实例的帧序号从烘焙纹理中采样对应顶点位置,直接替换原顶点位置输出
- 优势:不受骨骼数量限制,实现逻辑简单,适合低精度大量同模动画物体的场景(比如人群、小怪集群)
- 限制:仅支持固定循环动画,不支持运行时动态修改骨骼状态、动画变速/混叠等需求
补充提示:如果场景中同模的蒙皮网格数量少于20,不需要强行做单次绘制优化,普通多
SkinnedMesh的性能开销已经足够低,优化带来的收益远低于实现成本。
内容的提问来源于stack exchange,提问作者Bhushan Wagh
相关产品推荐
相关产品推荐

