threejs加载优化与指南针

加载优化与指南针

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第5天,点击查看活动详情

本篇文章主要介绍threejs优化加载效率以及指南针的实现,以上图是以往功能文章的整合图,特地去剪辑了一下~

这里为什么将优化与指南针放在一篇文章,由于两者都较简单,所以考虑合为一篇文章介绍~

这里提一下,对粒子感兴趣的可以看我前一篇文章!我写的非常详细,但是!我被限流了,没有被推荐到首页!创作不易,猪猪叹气~

指南针

效果如封面图所示,主要是两张图片跟随场景旋转同步角度实现,相对简单

代码解析:通过一张底图和指南针图片的叠加,通过帧动画计算相机的角度同步到指南针上进行旋转

Viewer.prototype.initCompass = function () {
  const znzbg = document.createElement('img')
  znzbg.src = '/static/images/znzd.png'
  znzbg.style.position = 'absolute'
  znzbg.style.right = '500px'
  znzbg.style.top = '80px'
  znzbg.style.width = '80px'
  znzbg.style.height = '80px'
  const znz = document.createElement('img')
  znz.src = '/static/images/znz.png'
  znz.style.position = 'absolute'
  znz.style.right = '510px'
  znz.style.top = '90px'
  znz.style.width = '60px'
  znz.style.height = '60px'
  this.znz = znz
  this.el.appendChild(znzbg)
  this.el.appendChild(znz)
  this.renderFunction.push(() => {
    var dir = new THREE.Vector3(-this.camera.position.x, 0, -this.camera.position.z).normalize()
    var theta = Math.atan2(-dir.x, -dir.z)
    if (this.znz) this.znz.style.transform = `rotate(${THREE.Math.radToDeg(theta) + 45}deg)`
  })
}
复制代码

threejs加载优化

有经验的同学都有一个困扰,就是场景一大,每次加载都需要花很多时间。如果多场景同时加载到内存中,则会出现卡顿。怎么解决这个问题呢?

模型瓦片加载

第一次加载场景,需要解析模型文件,解析文件的时间没办法优化,但是我们可以将模型文件碎片化-瓦片加载。这里肯定有同学会问,以瓦片的形式加载好像也只是拆分加载,不会影响我们加载场景的时间~

不妨想一想,如果我们将碎片化的模型通过一个配置JSON去异步加载呢?这样是不是就会大大加快加载的速率,理想上是不是模型越碎片化加载的速率越快呢~

const configFile = 'data/parkBuildings/fbs/config.json' //配置JSON
return new Promise((resolve, reject) => {
  axios.get(configFile, {baseURL}).then(res => {
  //加载模型
  loadModel
}).catch(reason => {
  console.error(reason)
  reject(reason)
})
复制代码
//瓦片加载
Viewer.prototype.loadModel = (url, layeridx, callback) => {
  const that = gapp
  const loader = new GLTFExtensionLoader(gapp.manager)
  loader.workingPath = loader.workingPathForURL(url)
  loader.load(url)
    .then(gltf => {
      gltf.scene.traverse((e) => {
        e.layers.set(layeridx)
        e.userData.orglayers = layeridx //原始层级
      })
      const clips = gltf.animations || []//如果存在动画片段。提取所有动画片段
      gapp.addclips(clips, layeridx)
      gapp.glayers[layeridx].add(gltf.scene)
      callback ? callback(gltf.scene) : ''
    })
}
复制代码

瓦片加载存在的问题

通过上面的优化,是不是觉得还有缺陷~大家通常实现多场景的方案是一次性加载通过相机层级切换呢?还是切换完加载模型呢?

其实问题其中就显现出上面优化还存在的问题!我们在需要切换多场景的需求时,如果通过一次性加载完,将其存储于内存中,虽然通过相机调整层级无需多次加载,但是存在内存占用过大卡顿问题。所以通过第一个方案我们还是无法最大限度解决加载快并不卡顿的问题。当然我们如果切换场景才去做加载的话未免用户体验也太差了,这个方案当然是被pass。那我们应该怎么做才能鱼和熊掌兼得呢!

加载优化

不知道大家对indexDb是否熟悉,indexDb作为浏览器一个非关系型数据库。相比平时大家也都不会涉及到,这里介绍一个indexDb对于场景这一块如何起到优化的作用!

indexDb

  1. 模型文件解析后的JSON通过有几十兆到几百兆不等,回关浏览器所常用的存储是无法满足这么大的容量
  2. indexDb恰巧可以满足,并是类似mongodb的数据库。我们大可将文件名及解析后的对象以一一对应的形式存储到indexDb中
  3. 这样我们即可在第一次加载完所有场景模型后,将所有的瓦片解析对象通过threejs的toJSON转为特定的json格式以键值对的形式存储于indexDb中。
  4. 二次加载以及切换只需按照配置对象取出indexDb中的对应瓦片的对象通过threejs的fromJSON方法解析json转为对象格式并add到场景中
//转化为threejs特有的json格式
 scene.toJSON() 
 (了解过json的同学可以发现,threejs为了缩小大小,将瓦片对象最大限度的拆分材质等,通过id关联并保存为json)
//解析json转为对象
Viewer.prototype.fromJSON = function (json, layeridx, isRayobj) {
  return new Promise((resolve, reject) => {
    // 解析 json 对象
    let loader = new THREE.ObjectLoader();
    let loadedMesh = loader.parse(json);
    let scene = this.mergeToMaterialsMap(loadedMesh, true)
    resolve(scene)
  })
}
复制代码

场景优化方案总结

<瓦片异步加载> <存储转化瓦片对象> <解析瓦片对象免去重复解析的工作> <多场景切换无需重新解析模型> <一次加载一个场景减少内存占用>

总结

创作不易,猪猪叹气! 对threejs感兴趣的同学可以关注我的专栏,不定时更新threejs相关的案例以及方案~ threejs专栏

猜你喜欢

转载自juejin.im/post/7085167057792155662
今日推荐