THREEJS导入外部OBJ+MTL后内存优化

背景

        近期做一个关于石油化工的3D数字孪生项目,模型均为客户那边提供,模型文件是obj+mtl+jpg组合。因为有较多局部控制动画,所以建模被要求拆分成多个obj模型,然后在场景中自动组合使用。

现象

用THREEJS加载模型添加到场景中后发现两个问题:

1.渲染速度很慢;

2.如果浏览器同时开两个或窗口,那么会出现黑屏闪一下,然后页面就全白了,场景也都没了(如下图所示)。

 

调查

经过调查发现,打开一个窗口时,GPU内存被占用3.6G,打开两个的时候直接飙到5.2G,我这里GPU专用内存是6G,所以打开三个的时候直接就蹦了,所以就出现了黑屏的现象。

分析

        我查看了客户提供的资源文件总大小,大概40M左右(obj+mtl+jpg),所以感觉应该不是导入文件过大问题。

        无意间修改了一个mtlLoader的代码,错把objLoader.setMaterials(material)给注释掉了,导致场景中模型没显示任何贴图。

 

 但却惊奇的发现,GPU内存只占用了0.2G。

         所以我就大胆猜测,内存应该是都被导入的mtl占用了。经过不带mtl只导入obj的测试,验证了我的猜测是对的。

        有了方向,基本就成功了一半,所以接下来开始对mtlLoader中onLoad进来的material对象进行分析。

        ThreeJS的MTLLoader中onLoad返回的是一个MaterialCreator对象,其中包含了materials和materialsInfo两个属性,一个存放mesh对象使用的材质对象,一个存放对应的mtl文件内容(如下图)。

        一个mtl文件解析成一个MaterialCreator,一个MaterialCreator的materials中又有多个材质对象,所以好几G内存的占用,会不会是因为材质对象中有重复的。因为我记得THREEJS中,相同的对象不要重复创建,尽量使用clone,这样能提高性能。材质对象,如果是多个,那么就会被渲染多次。如果是相同的,通过clone,那是不是就渲染一次了。

        带着这个疑问,我去查看了一下mtl源文件。发现确实是好多相同的描述出现在不同的mtl文件中,比如newmtl Material__2,这种描述,好多mtl文件中都有,有些是完全一样的内容,有些虽然名一样,但是内容不一样。那么这些一样的,处在不同mtl文件中的,在被threejs解析添加到场景后,不就是按独立的材质被单独渲染么,明明是一样的内容,却按新的来渲染,这不就造成了资源浪费,消耗了很多内存么,渲染次数多了,渲染速度自然就慢了。

解决

        修改了一下mtlLoader的代码,创建一个map(什么都可以,不一定是map),用materialsInfo转成MD5作为key,用对应的materials作为value,加载一个缓存一个,如果当前加载的材质对象在缓存中存在,则直接clone一个,否则作为新的存入缓存。

          核心代码:

    // 相同材质采用克隆方式,不单独渲染,只需渲染45个材质对象,内存占用优化到1G,渲染速度3秒左右
    const mtlCache = new Map()
    shNameList.forEach(name => {
      mtlLoader.load('model/sh/整体罐区/' + name + '.mtl', (material) => {
        material.preload()
        Object.keys(material.materialsInfo).forEach(k => {
          const key = md5(JSON.stringify(material.materialsInfo[k]), 32)

          if (mtlCache.has(key)) {
            material.materials[k].copy(mtlCache.get(key))
          } else {
            mtlCache.set(key, material.materials[k])
          }
        })
        const objLoader = new OBJLoader()
        objLoader.setMaterials(material)
        objLoader.load('model/sh/整体罐区/' + name + '.obj', (object) => {
          ts.scene.add(object)
        })
      })
    })
    console.log(mtlCache)

    // 单独渲染材质对象,需渲染261个材质对象,占用3.6G内存,渲染花费9秒左右
    // const mtlList = []
    // shNameList.forEach(name => {
    //   mtlLoader.load('model/sh/整体罐区/' + name + '.mtl', (material) => {
    //     console.log(material)
    //     const objLoader = new OBJLoader()
    //     material.preload()
    //     Object.keys(material.materials).forEach(k => {
    //       mtlList.push(material.materials[k])
    //     })
    //     objLoader.setMaterials(material)
    //     objLoader.load('model/sh/整体罐区/' + name + '.obj', (object) => {
    //       ts.scene.add(object)
    //     })
    //   })
    // })
    // console.log(mtlList)

对比

优化前:

优化后:

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: 在使用three.js加载.obj和.mtl文件之前,您需要先确保您已经正确地导入three.js库,并且已经创建了一个WebGL渲染器和一个场景。 加载.obj和.mtl文件的过程分为两个步骤: 第一步是加载.obj文件。您可以使用THREE.OBJLoader()来加载.obj文件。首先,创建一个OBJLoader的实例,并且使用load()方法来加载.obj文件。例如: var objLoader = new THREE.OBJLoader(); objLoader.load('model.obj', function(object) { // 加载成功后的回调函数 scene.add(object); // 将加载的模型添加到场景中 }); 在上述代码中,我们使用.load()方法来加载.obj文件,并且在加载成功之后使用回调函数来添加到场景中。 第二步是加载.mtl文件。.mtl文件是指定.obj文件材质的文件。您还需要使用MTLLoader来加载.mtl文件,并且与OBJLoader一起使用。首先,创建一个MTLLoader的实例,并且使用load()方法来加载.mtl文件。然后,在OBJLoader的回调函数中,使用setMaterials()方法来设置材质。例如: var mtlLoader = new THREE.MTLLoader(); mtlLoader.load('model.mtl', function(materials) { materials.preload(); // 进行预加载 objLoader.setMaterials(materials); // 设置材质 objLoader.load('model.obj', function(object) { // 加载成功后的回调函数 scene.add(object); // 将加载的模型添加到场景中 }); }); 在上述代码中,我们首先加载.mtl文件,使用预加载和设置材质,然后在加载.obj文件时使用OBJLoader,并将加载结果添加到场景中。 以上就是使用three.js加载.obj和.mtl文件的基本过程。您可以根据实际场景和需求进行进一步的调整和扩展。 ### 回答2: 要加载OBJMTL文件,你首先需要在Three.js导入OBJLoader和MTLLoader模块。你可以通过在HTML文件中包含相关的脚本来实现。一旦你导入了这些模块,你可以使用这些模块来加载并显示OBJMTL文件。 首先,你需要创建一个场景和渲染器。创建一个用于显示模型的渲染器实例,并将其添加到页面中的某个元素上。然后,创建一个相机并设置其位置和方向。 接下来,你需要使用OBJLoader来加载OBJ文件。使用OBJLoader的load方法来加载OBJ文件,并指定加载完成后的回调函数。在回调函数中,你可以获取到加载的几何体数据。将几何体添加到场景中,并根据需要设置其位置、旋转和缩放。 然后,使用MTLLoader来加载对应的MTL文件。使用MTLLoader的load方法来加载MTL文件,并指定加载完成后的回调函数。在回调函数中,你可以获取到加载的材质数据。将材质应用到之前加载的几何体上。 最后,将场景渲染到页面中。在渲染循环中使用渲染器的render方法来将场景与相机进行渲染。 总结来说,使用Three.js加载OBJMTL文件需要导入OBJLoader和MTLLoader模块,创建场景和渲染器,加载OBJ文件并添加到场景中,加载MTL文件并应用到OBJ几何体上,最后渲染场景到页面中。 ### 回答3: 要加载.obj和.mtl文件,我们可以使用Three.js库中的OBJLoader和MTLLoader。 首先,我们需要在HTML文件中引入Three.js库: ```html <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.js"></script> ``` 接下来,我们创建一个场景、相机和渲染器: ```javascript var scene = new THREE.Scene(); var camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 1000); var renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); ``` 然后,我们使用MTLLoader加载.mtl文件和OBJLoader加载.obj文件: ```javascript var mtlLoader = new THREE.MTLLoader(); mtlLoader.load('path/to/file.mtl', function(materials) { materials.preload(); var objLoader = new THREE.OBJLoader(); objLoader.setMaterials(materials); objLoader.load('path/to/file.obj', function(object) { scene.add(object); }); }); ``` 在上面的代码中,我们需要将'path/to/file.mtl'和'path/to/file.obj'替换为实际的文件路径。 最后,我们可以添加灯光和动画,并在渲染循环中更新场景: ```javascript var light = new THREE.PointLight(0xFFFFFF, 1, 100); light.position.set(0, 0, 5); scene.add(light); function animate() { requestAnimationFrame(animate); renderer.render(scene, camera); } animate(); ``` 这样,我们就可以成功加载并显示.obj和.mtl文件了。 请注意,由于加载外部文件需要进行网络请求,所以在本地开发时需要确保跨域访问控制(CORS)设置正确。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值