童年原是一生最美妙的阶段,那时的孩子是一朵花,也是一颗果子,是一片懵懵懂懂的聪明,一种永远不息的活动,一股强烈的欲望。——巴尔扎克
现在位置:首页 > 网站建设 > 转载 > 使用ThreeJs搭建BIM模型浏览器,第九步-性能优化(2)

使用ThreeJs搭建BIM模型浏览器,第九步-性能优化(2)

玉龙之乡  转载  2020-10-16  10882  0评论

感谢网友给的建议。

我之前一直是使用threejs的102dev版本,以为geometry共享了就行了,但是这并不是threejs里面所说的Instance。在新的threejs版本中,新增加了几个很有意思的Instance类,这里重点挑InstancedMesh来说。InstancedMesh与使用一个geometry共享创建出Mesh是不一样的,InstancedMesh最终达到的效果是一次Drawcall,而共享geometry创建出来的Mesh并无此效果,效率跟不共享创建Mesh渲染性能上没有太大区别,只是可能会省点内存。

InstancedMesh是R110之后出现
官网简介:
A special version of Mesh with instanced rendering support. Use InstancedMesh if you have to render a large number of objects with the same geometry and material but with different world transformations. The usage of InstancedMesh will help you to reduce the number of draw calls and thus improve the overall rendering performance in your application.
大量相同的几何,会减少绘图调用的次数,大量提高页面性能;

参考文档:

http://www.qmodel.cn/threejs/three/docs/index.html#api/en/objects/InstancedMesh

查看示例:

http://www.qmodel.cn/threejs/three/examples/?q=instanc#webgl_instancing_raycast

用法:


  1. var geometry = new THREE.SphereBufferGeometry( 0.2 );
  2. var material = new THREE.MeshPhongMaterial( { flatShading: true } );
  3. var amount=80
  4. var count=amount*amount*amount;
  5. var mesh = new THREE.InstancedMesh( geometry, material, count );
  6. var i = 0;
  7. var offset = ( amount - 1 ) / 2;
  8. var transform = new THREE.Object3D();
  9. for ( var x = 0; x < amount; x ++ ) {
  10. for ( var y = 0; y < amount; y ++ ) {
  11. for ( var z = 0; z < amount; z ++ ) {
  12. transform.position.set( offset - x, offset - y, offset - z );
  13. transform.updateMatrix();
  14. mesh.setMatrixAt( i ++, transform.matrix );
  15. }
  16. }
  17. }
  18. scene.add( mesh );

点击获取InstancedId,官方示例


  1. // move
  2. function onMouseMove( event ) {
  3. event.preventDefault();
  4. mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
  5. mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
  6. }
  7. // render
  8. function render() {
  9. raycaster.setFromCamera( mouse, camera );
  10. var intersection = raycaster.intersectObject( mesh );
  11. if ( intersection.length > 0 ) {
  12. // 获取当前实例的ID 索引
  13. var instanceId = intersection[ 0 ].instanceId;
  14. // 根据当前的索引 获取当前的矩阵
  15. mesh.getMatrixAt( instanceId, instanceMatrix );
  16. // 更新矩阵
  17. matrix.multiplyMatrices( instanceMatrix, rotationMatrix );
  18. // 设置拾取当前几何的矩阵
  19. mesh.setMatrixAt( instanceId, matrix );
  20. // 更新矩阵
  21. mesh.instanceMatrix.needsUpdate = true;
  22. }
  23. renderer.render( scene, camera );
  24. }

实际测试性能效率,当使用InstancedMesh时,如上述代码。如果使用的是Mesh,我的渣渣电脑已经渲染不出来了。

 

如何解决使用InstancedMesh后,构件选择的问题?通过射线相交,可以取到Instance实例的索引ID,可使用ID来找到对应的构件。大家多看官方示例代码就可以了。

补充一点思路,如果网友有更好的方案也欢迎留言。

隐藏构件,把矩阵设置为0矩阵隐藏某个instanceId,0矩阵为[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,]

选择构件:可以考虑把InstancedMesh的geometry和被Pick到的Matrix构造一个新的InstancedMesh放在对应的位置,材质替换为高亮选中的材质,且把原来的Matrix设置为0矩阵,则实现隐藏。实际上目前120版本,Mesh和InstancedMesh还是有不少BUG的,建议全部使用InstancedMesh。

透明度和颜色设置:与选择构件同理,不过考虑到还原操作,对数据的管理会更复杂一些。这里会特征一些内存,效率上也有所降低,最终效果如何,全看编码人员的控制力了。

 

Merge之后也可以实现Drawcall的减少,且可以实现不同材质的合并。我就尝试过把一整个建筑模型合并成一个geometry,没错,性能提升巨大,且内存锐减一半!merge可以实现不同材质的几何直接合并,但是因为group的处理,还是会拆分为多次drawcall,没有太多作用。关于merge的总结可以看一下后面的介绍。

对比geometry.merge和Instance实例化、普通Mesh。以一个5万构件的全专业(建筑、结构、给排水、暖通、电气)的住宅塔楼为例(复用率还比较高的)。

  Instance实例化几何体 Merge合并几何体 普通Mesh
Material 相同 相同 可不同
Geometry 相同 可不同 可不同
单个控制 使用索引可实现 难以实现 容易实际
生成时间 快速 Merge需要计算消耗 一般。量大
渲染性能 较优 更优,极致减少drawcall
内存占用 极少 较少 较多
Drawcall次数 约1万次 1次 5万次

2020-9-11 

关于merge又有了新的发现。

我们来试一下常规情况,直接使用mesh加入12万个构件。渲染有多么困难,降到fps=6

无视材质的merge,看一下对比,geometry合并到8个里面,有所提升,fps=18,drawcall一次没少。

1、为什么drawcall一次没少呢?因为group没有把同材质的合并起来。由些判断drawcall跟group有关。

2、但是为什么性能会有提升?因为视锥剔除不需要计算了啊!

再进一步,把材质全都合并起来,一个geometry就一个material,会达到什么效果呢?看下图。怎么样?fps109,是不是起飞了?当然,它的不易之处在于,首先要按材质先分组,不同material的也就别合并了,技术上可行,但是合并没好处。

评论一下 分享本文 联系站长
 草原上的建筑- 草原上的建筑模型-玉龍之鄉
看完文章就评论一下!
挤眼 亲亲 咆哮 开心 想想 可怜 糗大了 委屈 哈哈 小声点 右哼哼 左哼哼 疑问 坏笑 赚钱啦 悲伤 耍酷 勾引 厉害 握手 耶 嘻嘻 害羞 鼓掌 馋嘴 抓狂 抱抱 围观 威武 给力
提交评论

清空信息
关闭评论

sitemap