THREE.JS监听模型渲染完毕方法-非正经操作

最近在开发过程中,遇到这么一个需求,就是需要在某个FBX模型load完毕后,需要toJSON给另一个接口。结果发现,toJSON的数据中,一直缺少image属性,导致那边接收到toJSON的模型后,没有贴图。后来发现,模型只有在场景中渲染完毕后再toJSON,才会有image。这就需要我们找到,模型渲染完毕的回调函数。

查看官方文档发现了这么个方法:onAfterRender

Object3D – three.js docs (threejs.org)

反正大致意思呢,就是那些场景中看得见的东西可以用这个方法,比如material、geometry、mesh、points、line等。而那些概念性的对象,就不能用这个方法,因为它们在视觉上没有什么可渲染的,比如Object3D、Group、Bone这种,就拿Group来说,组是一个组合的概念,将多个模型对象组合在一起,但组合本身并不是一种模型,也不需要渲染,所以Group没有geometry,也没有material。我们这里需要监听的模型就是fbx,fbx导入后,就是一个group,所以没法直接用这个方法。以上纯属个人理解,如有错误,欢迎指摘,随时更正。

继续查阅资料,发现了关于WebGLRenderer.compile和WebGLRenderer.initTexture的简介,没太看明白怎么用,直接写上也没生效,没有达到预期效果。如果有大神明白这个是干什么的以及怎么用,请赐教,小弟感激不尽(类似的简介就算了)。

后来想到用textureLoader,但toJSON的数据已经有了texture,即使用textureLoader再loader一下,也不会渲染出image,反而会多一次加载texture的操作。

最后实在没辙,决定自己写一个吧。不正经的需求,往往需要用不正经的手段来解决。

通过对模型对象的观察发现,只要group子类中material.map.image不为null的时候,就代表该材质贴图渲染完毕。于

是就有了以下不太正经的思路:

  1. 写一个方法,统计当前模型当前已渲染出来的material.map.image个数。

  1. 再写一个方法,递归自己,检测当前模型已渲染的image个数与上次个数是否一致,若不一致,则继续递归,若一致,则记录一致次数,我们假定一致次数为三次时,就表示image完全渲染,就执行回调函数。

  1. 这个一致次数,我测试用3就完全可以表示渲染完毕了,当然你可以根据自己实验来调整,因为不是什么正经方法,这里只是实验的结果,并不是什么科学的参数。

有了上述不太正经的思路,于是就有了下面不太正经的代码:

/**
 * 获取模型当前已渲染的贴图数
 * @param model       模型对象
 * @returns {number}  贴图数
 */
function getImageCount (model) {
  let iCount = 0

  model.traverse(item => {
    if (item instanceof THREE.Mesh) {
      if (Array.isArray(item.material)) {
        item.material.forEach(m => {
          if (m.map && m.map.image) {
            iCount++
          }
        })
      } else {
        if (item.material.map && item.material.map.image) {
          iCount++
        }
      }
    }
  })

  return iCount
}

/**
 *
 * @param model     模型对象
 * @param ic        贴图计数器
 * @param ec        重复次数
 * @param callback  回调函数
 */
function onAfterRender (model, ic, ec, callback) {
  const wait = 3

  let iCount = ic

  let equalCount = ec

  setTimeout(() => {
    const res = getImageCount(model)

    if (iCount === res) {
      if (equalCount === wait) {
        callback()
      } else {
        equalCount++
        onAfterRender(model, iCount, equalCount, callback)
      }
    } else {
      iCount = res
      equalCount = 0
      onAfterRender(model, iCount, equalCount, callback)
    }
  }, 100)
}

// 测试调用
new FBXLoader().load('model/fbx/test.fbx', res => {
  ts.scene.add(res)
  onAfterRender(res, 0, 0, () => {
    alert('渲染完毕')
    console.log('渲染完毕')
  })
})

测试结果

目前这么写还没发什么问题,若发现问题,后期会同步更新;若发现大坑,我会直接删除此篇文章。

如果有更好的方式,可以交流一下,感激不尽!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值