XR-FRAME的AR模式,先识别在加载模型,提升性能

208 阅读2分钟

懒得多说,上代码:

const lockTrackerEl = scene.createElement(xrFrameSystem.XRNode);
lockTrackerEl.addComponent(xrFrameSystem.ARTracker, {
    mode: 'Marker',
    src: i.src,
});
shadowNode.addChild(lockTrackerEl);

如上,其中shadowNode是自定义节点的引用 wxml:

<xr-shadow id="shadow-node" position="0 1 0" />

i.src自然就是追踪器识别的图片地址。

接下来是监听追踪器识别状态:

lockTrackerEl.event.add('ar-tracker-state', tracker => {
    // 获取当前状态和错误信息
    const {
        state,
        errorMessage
    } = tracker;
    console.log('>>> errorMessage', errorMessage)
    if (state === 2 && !waiting) {
        console.log('match')
        waiting = true;
    }
    // 动态创建添加GLTF资源
    i.trackerId?.forEach(id => {
        const item = gltfs.find(item => item.id === id)
        if (item) {
          const gltfNode = scene.createElement(xrFrameSystem.XRAssetLoad, {
            type: item.type,
            "asset-id": 'gltf-' + item.id,
            src: item.src
          });
          assetsNode?.addChild(gltfNode)
        }
    })
})

如上,gltfs是所有模型的列表,i.trackerId是追踪器对应的模型的id,assetsNode是资源加载器节点。

模型加载需要监控,等加载成功再设置模型的世界坐标为追踪器识别点的世界坐标。

this.loadedARGLTF = () => {
    this.handleArTrackerState(i, lockTrackerEl)
}

如上,loadedARGLTF方法是资源加载器加载完成的回调。

/**
   * 追踪器识别成功
   * @param {AssetItem} i 追踪器资源参数
   * @param lockTrackerEl 追踪器节点
   */
  handleArTrackerState(i: AssetItem, lockTrackerEl: XRNode) {
    const shadowNode = this.shadowNode
    const scene = this.scene
    const list = this.list
    const xrFrameSystem = wx.getXrFrameSystem();
    const gltfs = list.filter(i => i.type === AssetType.GLTF)

    // 追踪器和模型关系 一对多
    const lockItemEles: Element[] = []
    // 创建 TRS对应节点
    const lockParentEl = scene.createElement(xrFrameSystem.XRNode);
    i.trackerId?.forEach(id => {
      // 找到对应的模型配置参数
      const gltfParams = gltfs.find(p => p.id === id)
      if (gltfParams) {
        // 创建 SubTRS对应节点
        // 初始化 渲染到屏幕外 x:10000
        const lockItemEle = scene.createElement(xrFrameSystem.XRNode, {
          position: '10000 0 0',
        });
        lockParentEl.addChild(lockItemEle);
        // 记录 SubTRS对应节点
        lockItemEles.push(lockItemEle)

        // 创建 GLTF节点
        const gltfNode = scene.createElement(xrFrameSystem.XRGLTF, {
          model: 'gltf-' + id,
          position: gltfParams.position || '',
          scale: gltfParams.scale || '',
          rotation: gltfParams.rotation || '',
          'anim-autoplay': '',
        });
        // 监听 GLTF加载到屏幕的状态
        gltfNode.event.add('gltf-loaded', (event: { target: XRGLTF }) => {
          // console.log('>>> gltf-loaded', event)
          if (gltfParams.hasEvent) {
            this.addEvent(event.target, gltfParams.id)
          }
        });
        // GLTF节点添加到 SubTRS对应节点
        lockItemEle.addChild(gltfNode);
      }

    })
    // 模型添加到场景中
    shadowNode.addChild(lockParentEl);
    // 延迟30毫秒 ,保证节点已经挂载
    setTimeout(() => {
      // 将 lockTrackerEl 的世界矩阵信息同步到 lockItemEle
      const lockTrackerTrs = lockTrackerEl.getComponent(xrFrameSystem.Transform)
      lockItemEles.forEach(lockItemEle => {
        const lockItemTrs = lockItemEle.getComponent(xrFrameSystem.Transform)
        lockItemTrs.setLocalMatrix(lockTrackerTrs.worldMatrix);
        // 记录SubTRS
        this.gltfItemSubTRS.push(lockItemTrs)
      })
      // 记录TRS
      this.gltfItemTRS.push(lockParentEl.getComponent(xrFrameSystem.Transform))
      // 去除tracker监听
      shadowNode.removeChild(lockTrackerEl);
      // 开启旋转缩放逻辑
      scene.event.addOnce('touchstart', this.handleTouchStart.bind(this))
    }, 30);

  }

如上,逻辑略复杂。

总的来说就是在模型加载完成之后,直接添加GLTF元素节点。在GLTF节点渲染到场景之后,进行事件绑定。延迟30毫秒,设置GLTF节点的世界坐标(这一步按理说应该放在 GLTF节点渲染到场景之后 执行,不然可能有bug)。

最后移除追踪器,开启鼠标操作。

后续还要增加一个逻辑:如果页面有多个追踪器,应该全部移除。当前页面提供一个关闭操作,删除识别出来的模型,然后再把所有追踪器添加到场景。

这样可以反复使用。