【Threejs进阶教程

您所在的位置:网站首页 labview三维图片卡顿 【Threejs进阶教程

【Threejs进阶教程

2024-06-01 18:06| 来源: 网络整理| 查看: 265

点线面统计与内存管理 学习ThreeJS的捷径新人菜鸟对模型方面的常见误区(持续更新)数据量统计点面法线UV统计 提问:我该如何创建10万个不同颜色不同位置的盒子(Box)元素数量对内存的影响优化geometry和material数量合并几何体instancedMeshLOD TA之路

学习ThreeJS的捷径

本段内容会写在0篇以外所有的,本人所编写的Threejs教程中

对,学习ThreeJS有捷径 当你有哪个函数不懂的时候,第一时间去翻一翻文档 当你有哪个效果不会做的时候,第一时间去翻一翻所有的案例,也许就能找到你想要的效果 最重要的一点,就是,绝对不要怕问问题,越怕找找别人问题,你的问题就会被拖的越久

如果你确定要走WebGL/ThreeJS的开发者路线的话,以下行为可以让你更快的学习ThreeJS

没事就把所有的文档翻一遍,哪怕看不懂,也要留个印象,至少要知道Threejs有什么没事多看看案例效果,当你记忆的案例效果足够多时,下次再遇到相似问题时,你就有可能第一时间来找对应的案例,能更快解决你自己的问题上述案例不只是官网的案例,郭隆邦技术博客,跃焱邵隼,暮志未晚等站点均有不少优质案例,记得一并收藏 http://www.yanhuangxueyuan.com/ 郭隆邦技术博客 https://www.wellyyss.cn/ 跃焱邵隼 http://www.wjceo.com/ 暮志未晚(暮老的站点暂时挂了,请查阅他之前的threejs相关文档) 暮老的csdn首页 这三个站点是我最常逛的站点,推荐各位有事没事逛一下,看看他们的案例和写法思路,绝对没坏处 新人菜鸟对模型方面的常见误区(持续更新)

Q : 我文件只有几M,已经压缩过了,非常小了,为什么加载还是卡? A1 : 文件大小与占用内存一丁点关系都没有,只影响你加载出来模型的时间,尤其是在web端,你从后端要传到前端,自然是文件越小越好,glb格式的draco压缩,仅能改变文件大小,不能改变你模型的点线面数,更不可能降低你在运行时占用的内存 A2 : 卡之前先看看自己的电脑,如果是台式,保证台式机有独立显卡,且至少是GTX 960,RX580以上的级别,500块钱都舍不得花的人,不建议做WebGL,如果是笔记本,没有独立显卡的笔记本直接pass,换有显卡的电脑吧,部分笔记本会设置浏览器使用cpu渲染,cpu玩命渲染在叫苦连天,gpu一旁闲着看着火急火燎但是浏览器不用它,如果遇到笔记本有独立显卡,且至少在GTX 1050以上的水平,依然卡,建议检查一下是否浏览器使用GPU渲染

Q : 我模型也不大啊,也就场景里加载了几百个,怎么开始卡了 A : 同一个模型不要重复加载,尽量用clone去加载,后续我会在下面讲解clone的优缺点

loader.load('./model.gltf',obj=>{ let model = obj.scene; for(let i = 0;i let geometry = new THREE.BoxGeometry(1,1,1); // let geometry = new THREE.BoxGeometry(1,1,1,10,10,10); // let geometry = new THREE.BoxGeometry(1,1,1,100,100,100); // let geometry = new THREE.BoxGeometry(1,1,1,1000,1000,1000); let material = new THREE.MeshBasicMaterial({color:0xffffff * Math.random()}); let mesh = new THREE.Mesh(geometry,material); scene.add(mesh); console.log(geometry.attributes); }

我们写如上的这一段代码,然后,我们去控制台看看结果

在这里插入图片描述 我们可以发现,给盒子添加分段数的函数,可以增加三角面数,每次增加10倍,面数都会指数级增加,当我们分段数达到1000的时候,面数达到了恐怖的1803万,并且浏览器已经报出警告,说本次渲染时间达到了1957ms,已经很明白的告诉你要优化程序了

我们可以来算一下数据,就粗略的计算一下上述四种结果,仅点,法线,uv三个数组占用了多少内存

这里js使用了32位float来存储,占用4个字节,所以我们就这样算

分段数为1的盒子,面数为72/3 = 24,占用 (72 + 72 + 48) * 4 = 768字节 分段数位10的盒子,面数为2178 / 3 = 726,占用( 2178 + 2178 + 1452 ) * 4 / 1024 = 22.68k字节 分段数位100的盒子,面数为 183618/3 = 61206,占用 (183618 + 183618 + 122412 ) * 4 / (1024 * 1024) = 1.86M 字节 分段数位1000的盒子,面数为 18036018 /3 = 6012006,占用 (18036018 + 18036018 + 12024012) * 4 / (1024 * 1024) = 183.47M字节

180万面的模型,仅纸面数据就占了183.47M的内存,我们来看看浏览器的任务管理器

在浏览器中按下shift + esc来对比一下四次结果的不同内存占用 在这里插入图片描述 截图的时间,都是在按下刷新键的第五秒的时间,基本可以看出,threejs的程序就占了约70M的内存,而分段数1,10并没有多大差别,到了100时,差距达到了好几倍,到1000时,内存占用直接蹦到了1.25G 在这里插入图片描述 但是并不是说就一直稳定1.25G的内存占用,在程序稳定后,占用会降到如上图水平

虽然我们叫它模型,但是,模型也是一组数据,如果我们的数据量过于庞大,那么内存占用会随着模型增大而增加,笔者的机子是i7-10750,显卡是RTX 2070S,加载这个180万面都已经出现了帧率警告,更别提用没有显卡或者老机子来加载模型了

所以在建模之前,一定要考虑清楚用户群使用的设备的水平,不能盲目的增加模型的点线面,如果你拿到的模型,就是超过百万面的模型,那么要么要求使用你开发的threejs的程序的电脑,能达到加载100万面的模型不卡的水平,要么就对模型进行优化,减减面,删删细节即可

个人建议,任何的threejs项目,单个场景的所有模型总面数不要超过100万,最低可承受的设备水平为:GTX 1050

提问:我该如何创建10万个不同颜色不同位置的盒子(Box) 元素数量对内存的影响

我们进行下一个案例,本次我们来对比,同时加载10个和10万个box的内存占用

我们在场景中,创建10个和10万个不同颜色不同位置的盒子

let count = 10; // let count = 100000; for(let i = 0;i color:0xffffff * Math.random()}); let mesh = new THREE.Mesh(geometry,material); mesh.position.x = Math.random() * 100 - 50; mesh.position.y = Math.random() * 100 - 50; mesh.position.z = Math.random() * 100 - 50; scene.add(mesh); }

在这里插入图片描述 同时我们计算一下10万个盒子的点线面数 这次不仅渲染压力过大了,而且,出现了严重的掉帧情况,场景卡的异常,只有把相机对准盒子不密集的地方,才不怎么卡

一个盒子,面数为72/3 = 24,占用 (72 + 72 + 48) * 4 = 768字节 10万个盒子,768 * 100000 / (1024 * 1024) = 73.24M 字节

10万个盒子,比点线面数要低于上面的180万面的一个盒子,但是卡顿感已经超越了上面180万面的盒子

所以,有时候,控制元素数量,可以有效的降低场景的卡顿 单个场景建议元素数量 小于1000

部分建模软件,导出模型时,会无止境的累加组,一个物体可能要套个好几层的组,或者一堵墙上的每一块砖都创建一个元素,这样会导致,你的模型本来面数不多,但是元素几万几十万,一样卡的要死,如果你拿到的模型,是这些软件导出来的,请交给建模师处理后再使用

部分建模新人,会使用最基础的几何体,“堆模型”,你打开了之后,发现他的模型,细致看下去,全是一堆的圆柱,圆锥,盒子,球体堆出来的,远看是那么回事,近看一丁点技术含量都没有,遇到这种模型的,建议把建模的揍一顿(开个玩笑),这种模型一般元素数量会逆天的多,面数也少不了,这种模型明摆着就不是给程序用的,祝大家不要遇到这种模型为好

点名3D66网站的免费模型,笔者遇到的上述元素数量和面数均远超常规的模型,很大概率会在模型的某个地方,看到3d66字样,比如说模型的命名,贴图文件的命名,模型内部的元素等等,这里的免费模型是免费模型的重灾区,低质量模型遍地走,与其处理这些垃圾模型,还不如让建模师照着样子重做一个

优化geometry和material数量

刚刚我们说了,我们在场景中,创建10个和10万个不同颜色不同位置的盒子 我们可以用共享geometry和material的方式来对上述程序进行优化

// let count = 10; let count = 100000; let geometry = new THREE.BoxGeometry(1,1,1); for(let i = 0;i color:0xffffff * Math.random()}); let mesh = new THREE.Mesh(geometry,material); mesh.position.x = Math.random() * 100 - 50; mesh.position.y = Math.random() * 100 - 50; mesh.position.z = Math.random() * 100 - 50; scene.add(mesh); }

在这里插入图片描述 上述代码中,我们一共只创建了一个boxGeometry,所以我们减少了99999个geometry对象占用的内存,内存直接恢复到了35万,比起上面的64万,几乎优化了一倍

不仅geometry可以共享,材质也可以共享

// let count = 10; let count = 100000; let geometry = new THREE.BoxGeometry(1,1,1); let materials = [ new THREE.MeshBasicMaterial({color:0xffffff * Math.random()}), new THREE.MeshBasicMaterial({color:0xffffff * Math.random()}), new THREE.MeshBasicMaterial({color:0xffffff * Math.random()}), //你想创建几种随机的颜色,就多new几行 ] for(let i = 0;i color:0xffffff * Math.random()}); let mesh = new THREE.Mesh(geometry,material); for(let i = 0;imergeGeometries} from "../three/examples/jsm/utils/BufferGeometryUtils.js"; for(let i = 0;i let boxGeometry = new THREE.BoxGeometry(1,1,1); boxGeometry.translate( Math.random() * 100 - 50, Math.random() * 100 - 50, Math.random() * 100 - 50, ); geometries.push(boxGeometry); } let geometry = mergeGeometries(geometries); let material = new THREE.MeshBasicMaterial({color:0xffffff * Math.random()}); let mesh = new THREE.Mesh(geometry,material); scene.add(mesh); }

在这里插入图片描述 我们这次的内存达到了17万,更近了一步,我们来聊聊这个函数可以做什么

mergeGeometries( [ geometry ] ) 这个函数会把数组中所有的geometry的数据合并,也就是上面提到的 uv,position,normal这些数据合并

所以我们合并了100个之后,我们只需要创建1000个元素,就可以保证场景中有10万个盒子了,但是,本次操作中,我们增加了999个geometry,增加了997个材质,但是减少了990000个Mesh,这样我们的内存就顺理成章的降低了

同时,本次优化后,也再不卡顿了

合并元素,笔者仅建议在建模中完成,如果你的模型存在巨量元素,超过10万个,那么只建议在建模软件中完成

为什么不推荐程序来完成,首先我们要走一轮扩容数组的过程,有多少元素要执行多少次 * 4次,所以我们不可能每次运行程序前,先让程序花大量时间去合并几何体,所以合并几何体的工作,仅建议在建模软件中来完成,能交给建模师就尽量交给建模师

instancedMesh

有些人居然还觉得不够,还是觉得卡,毕竟优化完还有240万面,有好多人的机子依然带不起来,怎么办?

记得,这个是底牌,如果底牌都亮了,还是无法优化卡顿,那么赶紧,安排,让建模师,把模型优化下来!!!!!!!!

let count = 100000; let geometry = new THREE.BoxGeometry(1,1,1); let material = new THREE.MeshBasicMaterial({ //注意,使用instancedMesh,如果要用随机色,这里默认色要设置为白色 //如果你比较好奇为什么设置白色,你可以设置成任意颜色来查看结果即可 color:0xffffff }); let instancedMesh = new THREE.InstancedMesh(geometry,material,count); //instancedMesh想改变位置需要用特殊的方式,需要借助矩阵来改变 let matrix = new THREE.Matrix4(); for(let i = 0;i color:0xffffff * Math.random(), wireframe:true,//开启线框模式来观察物体变化 }); let lod = new THREE.LOD(); for(let i = 0;i


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3