three.js中的instancedMesh类优化渲染多个同网格材质的模子

[复制链接]
发表于 2025-9-27 18:11:36 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
three.js小白的学习之路。
在上上一篇博客中,简单验证了一下three.js中的网格共享。写的时间就有一些想法,假如说某个场景中有一万棵树,这些树共享一个geometry和material,有没有好的办法将其举行肯定程度上的渲染优化,以进步欣赏器的帧率。
查阅了了一番资料,找到一个three.js中的类——InstanceMesh。
先容

three.js的官网是如许先容的:InstancedMesh实例化网格是特别版本的Mesh(具有mesh的干系属性和方法),可以用来渲染大量的具有雷同多少体和材质、但具有差异天下变更的物体。InstancedMesh可以镌汰draw call的数量,从而提拔步伐团体的渲染性能。听起来就和我们想要的结果很搭。
构造函数:

  1. InstancedMesh(geometry: BufferGeometry, material: Material, count: Number)
复制代码
此中geometry和material表现雷同的多少体和材质,count表现有多少共用这些多少体和材质的mesh。
属性:

  1. .count
复制代码
实例的数量,可以在运行时改变这个数值,范围是[0, count],假如比count大,必要重新创建实例。
尚有其他如boundingBox(外边接矩形)、instancedColor(全部实例的颜色)、instanceMatrix(全部实例的局部变更)等。
方法:

  1. .computeBoundingBox(): undefined
  2. .computeBoundingSphere(): undefined
复制代码
分别对应boundingBox和boundingSphere两个属性。
  1. .dispose(): undefined
复制代码
开释实例的内部资源。
  1. .setMatrixAt( index: Number, matrix: Matrix4): undefined
复制代码
index表现是第几个实例Mesh,范围是[0, count);matrix表现一个给定的局部厘革矩阵。
其他的方法尚有setColorAt(设置已界说实例的颜色)、getColorAt、getMatrixAt等。
示例

假如说有这么一个山地场景,内里必要很多很多的树(这里为了方便,就使用一个平面+五个立方体表现一棵树)。每棵树共用geometry和material,但是每个数的平移旋转缩放均不雷同。

起首先正常的将其加载到场景内里:
  1. new GLTFLoader().load("/instanced.glb", (glb) => {
  2.   const mesh = glb.scene;
  3.   scene.add(mesh);
  4. });
复制代码
此时,场景的渲染帧率如图所示:

帧率大概只有14帧左右。
接下来对场景中每棵树,也就是立方体举行InstancedMesh实例化。
起首必要获取到全部的立方体Mesh:
  1. const instanceArr: Three.Mesh[] = [];  
  2. glb.scene.traverse((item) => {
  3.     if (item.isMesh && item.name.startsWith(name)) {
  4.       instanceArr.push(item);
  5.     }
  6.   });
复制代码
接下来就是对这些Mesh举行实例化:
  1.   const count = instanceArr.length; // 相同模型的总个数
  2.   // 创建InstancedMesh实例
  3.   const instanceMesh = new Three.InstancedMesh(
  4.     instanceArr[0].geometry,
  5.     instanceArr[0].material,
  6.     count
  7.   );
复制代码
通过数组的第一个元素,获取到共享的geometry和material。
然后就是在获取全部mesh的天下变更,并分别应用到每一个实例Mesh中:
  1.   const matrix = new Three.Matrix4();
  2.   const pos = new Three.Vector3();
  3.   const qua = new Three.Quaternion();
  4.   const sca = new Three.Vector3();
  5.   instanceArr.forEach((obj, i) => {
  6.     obj.getWorldPosition(pos);
  7.     obj.getWorldQuaternion(qua);
  8.     obj.getWorldScale(sca);
  9.     matrix.compose(pos, qua, sca); // 将此矩阵设置为由平移、旋转和缩放组成的变换。
  10.     instanceMesh.setMatrixAt(i, matrix); // 更新InstancedMesh中每个实例的变换矩阵
  11.   });
复制代码
末了将InstancedMesh到场到场景中,而且将原有的全部立方体mesh从场景中移除:
  1.   scene.add(instanceMesh);
  2.   instanceArr.forEach((obj) => {
  3.     obj.parent?.remove(obj); // 模型移除
  4.   });
  5.   instanceArr.splice(0, count); // 清除引用
复制代码
看一下运行结果:

可以看到,帧率险些拉满到60帧,提拔结果明显!
思考

做了一个简单的实验,就是将如今这个模子在一个性能较差(内存小,没有独显等)电脑上加载,使用InstancedMesh前后的性能对比如图所示:

使用前


使用后

可以看到,使用前后的帧率险些一样,都在3-5帧。
这就抛出了一个标题,就是假如模子本身就已经超出大概将要超出欣赏器的承载极限时,这个InstancedMesh方法险些是没有结果的。从这个角度来说,这个优化大概有一些范围性,即实用于那些有大量的共享网格和材质,且欣赏器帧率较低,但是欣赏器依然可以正常运行的环境。
总结

InstancedMesh类可以很好地优化那些具有大量的共享材质和网格,但是有差异平移旋转缩放的模子,就雷同于blender中使用了Alt+D复制了很多份,然后每一个模子的位姿又有差异。
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表