XR-FRAME 开始

news2024/11/16 20:34:49

目录

  • 新建一个XR组件
  • 在页面中使用这个组件
  • 添加一个物体
  • 来点颜色和灯光
  • 有点寡淡,加上图像
  • 让场景更丰富,环境数据
  • 动起来,加入动画
  • 还是不够,放个模型
  • 再来点交互
  • 组件通信,加上HUD
  • 虚拟 x 现实,追加AR能力
  • 识别人脸,给自己戴个面具
  • 手势,给喜欢的作品点赞
  • OSDMarker,给现实物体做标记
  • 2DMarker+视频,让照片动起来
  • 加上魔法,来点粒子
  • 后处理,让画面更加好玩
  • 分享给你的好友吧!
  • 之后的,就交给你的创意
  • 体验

xr-frame是一套小程序官方提供的XR/3D应用解决方案,基于混合方案实现,性能逼近原生、效果好、易用、强扩展、渐进式、遵循小程序开发标准。

在这一章中,我们将会带大家从头开始,用它构建一个XR小程序。

新建一个XR组件

首先创建项目,让我们选择小程序工程:
在这里插入图片描述

之后先在app.json加上一行配置:"lazyCodeLoading": "requiredComponents"。然后创建好组件文件夹,新建一个组件,然后修改组件的内容:

index.json:

{
  "component": true,
  "renderer": "xr-frame",
  "usingComponents": {}
}

index.wxml:

<xr-scene>
  <xr-camera clear-color="0.4 0.8 0.6 1" />
</xr-scene>

index.json中,我们指定了这个组件的渲染器是xr-frame;在index.wxml中,我们创建了一个场景xr-scene,并在其下添加了一个相机xr-camera
在这里插入图片描述

在页面中使用这个组件

创建完组件后,便可以在页面中使用它,让我们进入pages/index,修改它的jsonwxmlts文件:

json中:

{
  "usingComponents": {
    "xr-start": "../../components/xr-start/index"
  },
  "disableScroll": true
}

ts脚本中:

Page({
  data: {
    width: 300,
    height: 300,
    renderWidth: 300,
    renderHeight: 300,
  },
  onLoad() {
    const info = wx.getSystemInfoSync();
    const width = info.windowWidth;
    const height = info.windowHeight;
    const dpi = info.pixelRatio;
    this.setData({
      width, height,
      renderWidth: width * dpi,
      renderHeight: height * dpi
    });
  },
})

wxml中:

<view>
  <xr-start
    disable-scroll
    id="main-frame"
    width="{{renderWidth}}"
    height="{{renderHeight}}"
    style="width:{{width}}px;height:{{height}}px;"
  />
</view>

这里我们在脚本中设置了xr-frame组件需要渲染的宽高,然后传入wxml,并在其中使用了json中引用的组件进行渲染,目前效果如下,可见整个画布被xr-camera上设置的清屏颜色清屏了:
在这里插入图片描述

添加一个物体

接下来我们给场上添加一个物体,直接使用xr-mesh以及内置的几何数据、材质,创建一个立方体:

<xr-scene>
  <xr-mesh node-id="cube" geometry="cube" />
  <xr-camera clear-color="0.4 0.8 0.6 1" position="0 1 4" target="cube" camera-orbit-control />
</xr-scene>

这里我们给物体指定了一个node-id,作为节点的索引,之后修改xr-camerapositiontarget,让其始终看向这个立方体,最后再给相机加上camera-orbit-control属性,使得我们能对相机进行控制。
在这里插入图片描述

至此,一个立方体是渲染了出来,不过…为什么是黑色的?

来点颜色和灯光

物体黑色是因为在我们没有给xr-mesh指定材质时,用的是基于PBR效果的默认材质,需要光照,解决这个问题有两种方法,其一是不需要光照的物体,可以使用simple材质,这里就引入了材质定义:

<xr-asset-material asset-id="simple" effect="simple" uniforms="u_baseColorFactor:0.8 0.4 0.4 1" />
<xr-mesh node-id="cube" geometry="cube" material="simple" />

效果如下:
在这里插入图片描述

虽然这可以解决一些问题,但大部分情况下我们还是需要灯光的,就让我们把材质改回去,然后加上一些灯光吧:

<xr-light type="ambient" color="1 1 1" intensity="1" />
<xr-light type="directional" rotation="40 70 0" color="1 1 1" intensity="3" cast-shadow />

<xr-mesh
  node-id="cube" cast-shadow
  geometry="cube" uniforms="u_baseColorFactor:0.8 0.4 0.4 1"
/>
<xr-mesh
  position="0 -1 0" scale="4 1 4" receive-shadow
  geometry="plane" uniforms="u_baseColorFactor:0.4 0.6 0.8 1"
/>

这里我们加入了一个环境光和一个主平行光,调整了亮度和方向,同时加上了一个新的物体,再通过各个组件的caster-shadowreceive-shadow开启了阴影,效果如下:
在这里插入图片描述

有点寡淡,加上图像

虽然有了灯光,但只有纯色还是有一些寡淡,接下来我们尝试加入纹理,让场景的色彩更加丰富一些,这里需要用到资源加载器xr-asset-loadxr-assets

<xr-assets bind:progress="handleAssetsProgress" bind:loaded="handleAssetsLoaded">
  <xr-asset-load type="texture" asset-id="waifu" src="https://mmbizwxaminiprogram-1258344707.cos.ap-guangzhou.myqcloud.com/xr-frame/demo/waifu.png" />
</xr-assets>

<xr-mesh
  node-id="cube" cast-shadow
  geometry="cube" uniforms="u_baseColorMap: waifu"
/>

注意到我们在xr-assets上绑定了两个事件progressloaded,这便于开发者监听资源加载进度,然后按需做一些操作,比如资源加载完成后和wx:if协作再显示物体。默认情况下,我们采用渐进式策略,当资源加载完成后会自动应用到物体上:

methods: {
  handleAssetsProgress: function ({detail}) {
    console.log('assets progress', detail.value);
  },
  handleAssetsLoaded: function ({detail}) {
    console.log('assets loaded', detail.value);
  }
}

这次的修改效果如下:
在这里插入图片描述

当然,我们还可以用代码动态加载一张纹理,然后将其设置到物体上,这里以获取用户信息的头像为例:

data: {
  avatarTextureId: 'white'
},

methods: {
  handleReady: function ({detail}) {
    this.scene = detail.value;
    // 该接口已废弃,请授权后,采用 getUserInfo 代替。
    wx.getUserProfile({
      desc: '获取头像',
      success: (res) => {
        this.scene.assets.loadAsset({
          type: 'texture', assetId: 'avatar', src: res.userInfo.avatarUrl
        }).then(() => this.setData({avatarTextureId: 'avatar'}));
      }
    })
  }
}

根据 小程序用户头像昵称获取规则调整公告 wx.getUserProfile 于 2022 年 10 月 25 日 24 时后,被废弃

注意这里的handleReady,我们可以在xr-scene上绑定bind:ready="handleReady"触发。完成头像获取后,将数据设置为uniforms的来源:

<xr-mesh
  position="0 -1 0" scale="4 1 4" receive-shadow
  geometry="plane" uniforms="u_baseColorMap: {{avatarTextureId}}"
/>

效果如下:
在这里插入图片描述

让场景更丰富,环境数据

物体有了纹理,那么背景能不能也有纹理呢?当然可以。我们提供了环境元素xr-env来定义环境信息,配合以相机可以渲染天空盒,这里以框架内置的一个环境数据xr-frame-team-workspace-day为例:

<xr-env env-data="xr-frame-team-workspace-day" />

<xr-mesh
  node-id="cube" cast-shadow
  geometry="cube" uniforms="u_baseColorMap: waifu,u_metallicRoughnessValues:1 0.1"
/>

<xr-camera
  position="0 1 4" target="cube" background="skybox"
  clear-color="0.4 0.8 0.6 1" camera-orbit-control
/>

这里我们将xr-camerabackgournd设置为了skybox,同时调整了立方体的金属粗糙度,效果如下:
在这里插入图片描述

同时可以看到场景中的物体相机叠加了一层反射,就像是被环境影响了一样,这是因为环境数据里还包括一些IBL的信息,当然这个我们不在这里赘述了,有兴趣的可以详细阅读后面的章节。

天空盒除了图像,还支持视频,我们可以先加载一个视频纹理,然后覆盖掉环境信息中的sky-map

<xr-asset-load type="video-texture" asset-id="office" src="https://mmbizwxaminiprogram-1258344707.cos.ap-guangzhou.myqcloud.com/xr-frame/demo/videos/office-skybox.mp4" options="autoPlay:true,loop:true" />

<xr-env env-data="xr-frame-team-workspace-day" sky-map="video-office" />

同时除了这种天空盒,我们还支持2D背景,这个在做一些商品展示的时候会比较有用:

<xr-asset-load type="texture" asset-id="weakme" src="https://mmbizwxaminiprogram-1258344707.cos.ap-guangzhou.myqcloud.com/xr-frame/demo/weakme.jpg" />

<xr-env env-data="xr-frame-team-workspace-day" sky-map="weakme" is-sky2d />

效果如下:
在这里插入图片描述

动起来,加入动画

目前我们的整个场景还是静态的,接下来我们会加入一些动画让其丰富起来。这里要使用帧动画资源,让我们先创建一个资源目录,在其下创建一个json文件:

{
  "keyframe": {
    "plane": {
      "0": {
        "rotation.y": 0,
        "material.u_baseColorFactor": [0.2, 0.6, 0.8, 1]
      },
      "50": {
        "material.u_baseColorFactor": [0.2, 0.8, 0.6, 1]
      },
      "100": {
        "rotation.y": 6.28,
        "material.u_baseColorFactor": [0.2, 0.6, 0.8, 1]
      }
    },
    "cube": {
      "0": {
        "position": [-1, 0, 0]
      },
      "25": {
        "position": [-1, 1, 0]
      },
      "50": {
        "position": [1, 1, 0]
      },
      "75": {
        "position": [1, 0, 0]
      }
    }
  },
  "animation": {
    "plane": {
      "keyframe": "plane",
      "duration": 4,
      "ease": "ease-in-out",
      "loop": -1
    },
    "cube": {
      "keyframe": "cube",
      "duration": 4,
      "ease": "steps",
      "loop": -1,
      "direction": "both"
    }
  }
}

然后加载它,并引用到场上的两个物体中:

<xr-asset-load asset-id="anim" type="keyframe" src="/assets/animation.json"/>

<xr-mesh
  node-id="cube" cast-shadow anim-keyframe="anim" anim-autoplay="clip:cube,speed:2"
  geometry="cube" uniforms="u_baseColorMap: waifu,u_metallicRoughnessValues:1 0.1"
/>
<xr-mesh
  node-id="plane" position="0 -1 0" scale="4 1 4" receive-shadow anim-keyframe="anim" anim-autoplay="clip:plane"
  geometry="plane" uniforms="u_baseColorMap: {{avatarTextureId}}"
/>

<xr-camera
  position="0 1 6" target="plane" background="skybox"
  clear-color="0.4 0.8 0.6 1" camera-orbit-control
/>

这里我们将xr-cameratarget设置到了plane上,以防其跟随cube乱动。

注意因为是包内的json文件,所以需要在project.config.jsonsetting字段中增加 "ignoreDevUnusedFiles": false"ignoreUploadUnusedFiles": false配置参数!

还是不够,放个模型

看着这个场景,你可能也觉得缺了点什么,不错——都是方方正正的几何体,还是太单调了。所以在这里,我们将加载并使用glTF模型,来让场景更加丰富。为了让场景简洁,我们去掉原场景的所有物体,调整相机的target

<xr-asset-load type="gltf" asset-id="damage-helmet" src="https://mmbizwxaminiprogram-1258344707.cos.ap-guangzhou.myqcloud.com/xr-frame/demo/damage-helmet/index.glb" />
<xr-asset-load type="gltf" asset-id="miku" src="https://mmbizwxaminiprogram-1258344707.cos.ap-guangzhou.myqcloud.com/xr-frame/demo/miku.glb" />

<xr-gltf node-id="damage-helmet" model="damage-helmet" />
<xr-gltf model="miku" position="-0.15 0.75 0" scale="0.07 0.07 0.07" rotation="0 180 0" anim-autoplay />

<xr-camera
  position="0 1.5 4" target="damage-helmet" background="skybox"
  clear-color="0.4 0.8 0.6 1" camera-orbit-control
/>

这里我们加载了两个模型:一个静态但支持了所有PBR渲染的特性,一个简单一些但有动画,最后的效果如下:
在这里插入图片描述

再来点交互

渲染部分到这里说的就差不多了,但作为一个应用,和用户的交互必不可少。很多场景下开发者可能需要点击场景中的物体来做一些逻辑,所以我们提供了shape系列组件:

<xr-gltf
  node-id="damage-helmet" model="damage-helmet"
  id="helmet" mesh-shape bind:touch-shape="handleTouchModel"
/>
<xr-gltf
  model="miku" position="-0.15 0.75 0" scale="0.07 0.07 0.07" rotation="0 180 0" anim-autoplay
  id="miku" cube-shape="autoFit:true" shape-gizmo bind:touch-shape="handleTouchModel"
/>

我们给几个模型设置了id,添加上了不同形状的shape,一个mesh-shape可以完全匹配模型,但开销较高并有顶点限制,一个cube-shape开销较低,还可以打开debug开关shape-gizmo将它显示出来。最后,我们并绑定了对应的点击事件,之后便可以在脚本里写逻辑,完成相应的操作了:

handleTouchModel: function ({detail}) {
  const {target} = detail.value;
  const id = target.id;
  
  wx.showToast({title: `点击了模型: ${id}`});
}

之后在点击到对应物体时,便会弹出提示:
在这里插入图片描述

组件通信,加上HUD

虽然有了交互,但总不能让交互都是这种弹窗吧。很多时候我们会让交互和UI元素互相影响,但目前xr-frame尚未支持和小程序的UI元素混写(会在未来版本支持),但我们可以使用同层方案,而同层方案,就必然涉及到组件通信了。

xr-frame组件和父级的通信与传统组件基本一致,这里让我们用小程序的UI元素实现一下HUD。这里可能会有一些3D变换的知识,但不要紧,只是调用接口而已。

首先,让我们修改组件的wxml,为场景添加tick事件,并且为模型和相机都加上id方便索引。

<xr-scene bind:ready="handleReady" bind:tick="handleTick">
......
<xr-gltf
  node-id="damage-helmet" model="damage-helmet"
  id="helmet" mesh-shape bind:touch-shape="handleTouchModel"
/>
<xr-gltf
  model="miku" position="-0.15 0.75 0" scale="0.07 0.07 0.07" rotation="0 180 0" anim-autoplay
  id="miku" cube-shape="autoFit:true" shape-gizmo bind:touch-shape="handleTouchModel"
/>
<xr-camera
  id="camera" position="0 1.5 4" target="damage-helmet" background="skybox"
  clear-color="0.4 0.8 0.6 1" camera-orbit-control
/>
</xr-scene>

之后在组件的脚本中处理事件,编写逻辑:

handleReady: function ({detail}) {
  this.scene = detail.value;
  const xrFrameSystem = wx.getXrFrameSystem();
  this.camera = this.scene.getElementById('camera').getComponent(xrFrameSystem.Camera);
  this.helmet = {el: this.scene.getElementById('helmet'), color: 'rgba(44, 44, 44, 0.5)'};
  this.miku = {el: this.scene.getElementById('miku'), color: 'rgba(44, 44, 44, 0.5)'};
  this.tmpV3 = new (xrFrameSystem.Vector3)();
},
handleAssetsLoaded: function ({detail}) {
  this.triggerEvent('assetsLoaded', detail.value);
},
handleTick: function({detail}) {
  this.helmet && this.triggerEvent('syncPositions', [
    this.getScreenPosition(this.helmet),
    this.getScreenPosition(this.miku)
  ]);
},
handleTouchModel: function ({detail}) {
  const {target} = detail.value;
  this[target.id].color = `rgba(${Math.random()*255}, ${Math.random()*255}, ${Math.random()*255}, 0.5)`;
},
getScreenPosition: function(value) {
  const {el, color} = value;
  const xrFrameSystem = wx.getXrFrameSystem();
  this.tmpV3.set(el.getComponent(xrFrameSystem.Transform).worldPosition);
  const clipPos = this.camera.convertWorldPositionToClip(this.tmpV3);
  const {frameWidth, frameHeight} = this.scene;
  return [((clipPos.x + 1) / 2) * frameWidth, (1 - (clipPos.y + 1) / 2) * frameHeight, color, el.id];
}

这里我们在ready事件中通过id索引获取了需要的实例并存了下来,然后在每帧的tick事件中实时获取物体的世界坐标,将其转换为屏幕的位置,并且还加上了在用户点击时改变颜色color的效果。在最后,我们通过this.triggerEvent,从组件向页面发起了通信,一个是资源加载完成的事件assetsLoaded,一个是坐标更新的事件syncPositions。让我们看看在场景的脚本中是如何处理这些事件的:

data: {
  width: 300, height: 300,
  renderWidth: 300, renderHeight: 300,
  loaded: false,
  positions: [[0, 0, 'rgba(44, 44, 44, 0.5)', ''], [0, 0, 'rgba(44, 44, 44, 0.5)', '']],
},
handleLoaded: function({detail}) {
  this.setData({loaded: true});
},
handleSyncPositions: function({detail}) {
  this.setData({positions: detail});
},

可见只是简单地接受了事件,然后将其设置为data而已,那么这个data用在哪里呢,来看看页面的wxml

<view>
  <xr-start
    disable-scroll
    id="main-frame"
    width="{{renderWidth}}"
    height="{{renderHeight}}"
    style="width:{{width}}px;height:{{height}}px;"
    bind:assetsLoaded="handleLoaded"
    bind:syncPositions="handleSyncPositions"
  />

  <block wx:if="{{loaded}}" wx:for="{{positions}}" wx:for-item="pos" wx:key="*this">
    <view style="display: block; position: absolute;left: {{pos[0]}}px;top: {{pos[1]}}px;background: {{pos[2]}};transform: translate(-50%, -50%);">
      <view style="text-align: center;color: white;font-size: 24px;padding: 8px;">{{pos[3]}}</view>
    </view>
  </block>
</view>

也很简单,就是在xr-start组件上加上了事件的绑定,然后下面多了一些UI,在模型加载完毕后显示,并按照位置和颜色跟随模型移动,这可以认为是基于DOM的HUD。整个完成了,用户点击物体,会让这些HUD变色,效果如下:

注意这里的左侧效果截图是真机截图P上去的,因为工具暂不支持同层渲染!

在这里插入图片描述

虚拟 x 现实,追加AR能力

到这里,我们实现了3D场景的渲染和交互,但框架毕竟是叫做XR-frame,所以接下来我们就用内置的AR系统来改造一下这个场景,让它具有AR能力吧。改造非常简单,我们首先将所有的无关物体移除,然后使用ar-systemar-tracker,并修改一下xr-camera的相关属性is-ar-camerabackground="ar"就好:

<xr-scene ar-system="modes:Plane" bind:ready="handleReady">
  <xr-assets bind:loaded="handleAssetsLoaded">
    <xr-asset-load type="gltf" asset-id="anchor" src="https://mmbizwxaminiprogram-1258344707.cos.ap-guangzhou.myqcloud.com/xr-frame/demo/ar-plane-marker.glb" />
    <xr-asset-load type="gltf" asset-id="miku" src="https://mmbizwxaminiprogram-1258344707.cos.ap-guangzhou.myqcloud.com/xr-frame/demo/miku.glb" />
  </xr-assets>

  <xr-env env-data="xr-frame-team-workspace-day" />
  <xr-light type="ambient" color="1 1 1" intensity="1" />
  <xr-light type="directional" rotation="40 70 0" color="1 1 1" intensity="3" cast-shadow />

  <xr-ar-tracker mode="Plane">
    <xr-gltf model="anchor"></xr-gltf>
  </xr-ar-tracker>
  <xr-node node-id="setitem" visible="false">
    <xr-gltf model="miku" anim-autoplay scale="0.08 0.08 0.08" rotation="0 180 0"/>
  </xr-node>

  <xr-camera clear-color="0.4 0.8 0.6 1" background="ar" is-ar-camera />
</xr-scene>

注意这里我们开启的ar-system的模式为Plane,即平面识别,这种模式下相机不能被用户控制,需要将控制器、target等都删掉,同时ar-trackermode要和ar-system的完全一致。之后再脚本中写一点简单的逻辑即可:

handleAssetsLoaded: function({detail}) {
  wx.showToast({title: '点击屏幕放置'});
  this.scene.event.add('touchstart', () => {
    this.scene.ar.placeHere('setitem', true);
  });
}

目前AR系统只能在真机预览时生效,所以我们可以将其提交预览,最终效果如下(AR的案例效果都是P的):
在这里插入图片描述

识别人脸,给自己戴个面具

在初步了解了AR系统后,我们便可以尝试更多不同的模式来玩做一些好玩的效果。接下来的是人脸识别模式,为此我们只需要在上面的代码中改几句,就可以给自己带上Joker的面具(逃):

⚠️ 手势、人脸、躯体识别都需要基础库v2.28.1以上。

<xr-scene ar-system="modes:Face;camera:Front" bind:ready="handleReady" bind:tick="handleTick">
  <xr-assets bind:loaded="handleAssetsLoaded">
    <xr-asset-load type="gltf" asset-id="mask" src="https://mmbizwxaminiprogram-1258344707.cos.ap-guangzhou.myqcloud.com/xr-frame/demo/jokers_mask_persona5.glb" />
  </xr-assets>

  <xr-env env-data="xr-frame-team-workspace-day" />
  <xr-light type="ambient" color="1 1 1" intensity="1" />
  <xr-light type="directional" rotation="40 70 0" color="1 1 1" intensity="3" />

  <xr-ar-tracker mode="Face" auto-sync="43">
    <xr-gltf model="mask" rotation="0 180 0" scale="0.5 0.5 0.5" />
  </xr-ar-tracker>

  <xr-camera clear-color="0.4 0.8 0.6 1" background="ar" is-ar-camera />
</xr-scene>

这里我们将ar-systemmodes改为了Face,并且新增设置了camera属性为Front,表示开启前置相机(注意前置相机在客户端8.0.31后才支持,这里仅做演示)。同时在ar-tracker这边,我们将mode改为了和ar-system一样的Face,并追加了属性auto-sync,这是一个数字数组,表示将识别出的面部特征点和对应顺序的子节点绑定并自动同步,具体的特征点可见组件文档详细描述。

手势,给喜欢的作品点赞

除了人脸之外,我们也提供了躯体人手识别,用法都大同小异,但人手除了上面所属的特征点同步,还提供了“手势”识别,这个比较有趣,让我们来看看:

<xr-scene ar-system="modes:Hand" bind:ready="handleReady" bind:tick="handleTick">
  <xr-assets bind:loaded="handleAssetsLoaded">
    <xr-asset-load type="gltf" asset-id="cool-star" src="https://mmbizwxaminiprogram-1258344707.cos.ap-guangzhou.myqcloud.com/xr-frame/demo/cool-star.glb" />
  </xr-assets>

  <xr-env env-data="xr-frame-team-workspace-day" />
  <xr-light type="ambient" color="1 1 1" intensity="1" />
  <xr-light type="directional" rotation="40 70 0" color="1 1 1" intensity="3" cast-shadow />

  <xr-ar-tracker id="tracker" mode="Hand" auto-sync="4">
    <xr-gltf model="cool-star" anim-autoplay />
  </xr-ar-tracker>

  <xr-camera clear-color="0.4 0.8 0.6 1" background="ar" is-ar-camera />
</xr-scene>

wxml这里我们换了个模型,并且将ar-systemar-tracker的模式都换成了Hand,并修改了ar-tracker的特征点还加上了个id方便索引,最后还给scene绑定了tick事件,而接下来就是js逻辑了:

handleAssetsLoaded: function ({detail}) {
  this.setData({loaded: true});

  const el = this.scene.getElementById('tracker');
  this.tracker = el.getComponent(wx.getXrFrameSystem().ARTracker);
  this.gesture = -1;
},
handleTick: function() {
  if (!this.tracker) return;
  const {gesture, score} = this.tracker;
  if (score < 0.5 || gesture === this.gesture) {
    return;
  }

  this.gesture = gesture;
  gesture === 6 && wx.showToast({title: '好!'});
  gesture === 14 && wx.showToast({title: '唉...'});
}

最重要的是handleTick方法,在每一帧我们拿到tracker的引用,然后获得它的属性gesturescore,其中gesture为手势编号而score为置信度。具体的手势编号可见组件文档,这里我先用置信度过滤了一遍,随后依据手势gesture的值(6为赞,14为踩)来提示不同信息,效果如下:

OSDMarker,给现实物体做标记

人体之外还有的能力就是两个marker了。其一是OSD Marker,一般以一个现实中物体的照片作为识别源,来识别出这个物体的在屏幕中的二维区域,我们已经做好了到三维空间的转换,但开发者需要自己保证tracker下模型的比例是符合识别源的。OSD模式在识别那些二维的、特征清晰的物体效果最好,比如广告牌。

这里是默认示例资源,你可以换成自己的照片和视频,如果只是想要尝试,直接复制访问src的地址到浏览器打开即可。

<xr-scene ar-system="modes:OSD" bind:ready="handleReady">
  <xr-assets bind:loaded="handleAssetsLoaded">
    <xr-asset-material asset-id="mat" effect="simple" uniforms="u_baseColorFactor: 0.8 0.6 0.4 0.7" states="alphaMode:BLEND" />
  </xr-assets>

  <xr-node>
    <xr-ar-tracker
      mode="OSD" src="https://mmbizwxaminiprogram-1258344707.cos.ap-guangzhou.myqcloud.com/xr-frame/demo/marker/osdmarker-test.jpg"
    >
      <xr-mesh geometry="plane" material="mat" rotation="-90 0 0" />
    </xr-ar-tracker>
  </xr-node>

  <xr-camera clear-color="0.4 0.8 0.6 1" background="ar" is-ar-camera />
</xr-scene>

这里我们把ar-system的模式改为了OSD,相应的ar-tracker的模式也改为了OSD,这种模式下需要提供src,也就是要识别的图像。并且这次我们使用了一个效果为simple的材质,因为不需要灯光,同时为了更好看效果,在materialstates设置了alphaMode:BLEND,即开启透明混合,然后将uniforms设置颜色u_baseColorFactor,并且注意其透明度为0.7。最终效果如下:

2DMarker+视频,让照片动起来

最后的能力就是2D Marker,其用于精准识别有一定纹理的矩形平面,我们可以将其配合视频纹理,只需要非常简单的代码就可以完成一个效果,首先是wxml

这里是默认示例资源,你可以换成自己的照片和视频,如果只是想要尝试,直接复制访问src的地址到浏览器打开即可。

<xr-scene ar-system="modes:Marker" bind:ready="handleReady">
  <xr-assets bind:loaded="handleAssetsLoaded">
    <xr-asset-load
      type="video-texture" asset-id="hikari" options="loop:true"
      src="https://mmbizwxaminiprogram-1258344707.cos.ap-guangzhou.myqcloud.com/xr-frame/demo/xr-frame-team/2dmarker/hikari-v.mp4"
    />
    <xr-asset-material asset-id="mat" effect="simple" uniforms="u_baseColorMap: video-hikari" />
  </xr-assets>

  <xr-node wx:if="{{loaded}}">
    <xr-ar-tracker
      mode="Marker" bind:ar-tracker-switch="handleTrackerSwitch"
      src="https://mmbizwxaminiprogram-1258344707.cos.ap-guangzhou.myqcloud.com/xr-frame/demo/xr-frame-team/2dmarker/hikari.jpg"
    >
      <xr-mesh node-id="mesh-plane" geometry="plane" material="mat" />
    </xr-ar-tracker>
  </xr-node>

  <xr-camera clear-color="0.4 0.8 0.6 1" background="ar" is-ar-camera />
</xr-scene>

这里我们把ar-system的模式改成了Marker,随后将ar-tracker的类型也改为了Marker,并且换了一个识别源,然后加载一个准备好的视频纹理,并将simple材质的颜色换为了纹理u_baseColorMap,同时关闭了混合。注意我们使用了变量loaded来控制ar-tracker的显示并绑定了事件ar-tracker-switch,这是为了在脚本中处理:

handleAssetsLoaded: function ({detail}) {
  this.setData({loaded: true});
},
handleTrackerSwitch: function ({detail}) {
  const active = detail.value;
  const video = this.scene.assets.getAsset('video-texture', 'hikari');
  active ? video.play() : video.stop();
}

在视频加载完成后再显示内容,并且在ar-tracker-switch事件也就是识别成功后在播放视频,优化体验,最终效果如下:

加上魔法,来点粒子

光是播放视频似乎还是有点单调,这里我们可以请出粒子系统制造一些魔法来让整个场景更加生动:

  ......
  <xr-asset-load type="texture" asset-id="point" src="https://mmbizwxaminiprogram-1258344707.cos.ap-guangzhou.myqcloud.com/xr-frame/demo/particles/point.png" />
  ......
  <xr-node wx:if="{{loaded}}">
    <xr-ar-tracker
      mode="Marker" bind:ar-tracker-switch="handleTrackerSwitch"
      src="https://mmbizwxaminiprogram-1258344707.cos.ap-guangzhou.myqcloud.com/xr-frame/demo/xr-frame-team/2dmarker/hikari.jpg"
    >
      <xr-mesh node-id="mesh-plane" geometry="plane" material="mat" />
      <xr-particle
        capacity="500" emit-rate="20"
        size="0.03 0.06" life-time="2 3" speed="0.04 0.1"
        start-color="1 1 1 0.8" end-color="1 1 1 0.2"
        emitter-type="BoxShape"
        emitter-props="minEmitBox:-0.5 0 0.5,maxEmitBox:0.5 0.2 0,direction:0 0 -1,direction2:0 0 -1"
        texture="point"
      />
    </xr-ar-tracker>
  </xr-node>
......

在上一步2DMarker视频的基础上,我们加上了xr-particle元素,使用了新加载的贴图pointboxShape发射器以及其他参数来生成粒子,最终效果如下(当然限于本人美术功底效果非常一般,相信你可以随便调一调完爆我233):

后处理,让画面更加好玩

在主体渲染结束后,好像还是有些单调,缺乏一种和现实世界的明确分离感,这时候就可以用全屏后处理来实现一些更好玩的效果:

  ......
  <xr-asset-load asset-id="anim" type="keyframe" src="/assets/animation.json"/>
  ......
  <xr-asset-post-process
    asset-id="vignette" type="vignette" data="intensity:1,smoothness:4,color:1 0 0 1"
    anim-keyframe="anim" anim-autoplay
  />
  <xr-camera clear-color="0.4 0.8 0.6 1" background="ar" is-ar-camera post-process="vignette" />

这里我为相机应用了一个渐晕vignette后处理效果,并为其加上了帧动画控制参数:

{
  "keyframe": {
    "vignette": {
      "0": {
        "asset-post-process.assetData.intensity": 0
      },
      "100": {
        "asset-post-process.assetData.intensity": 1
      }
    }
  },
  "animation": {
    "vignette": {
      "keyframe": "vignette",
      "duration": 2,
      "ease": "ease-in-out",
      "loop": -1,
      "direction": "both"
    }
  }
}

最终效果如下:

分享给你的好友吧!

好,终于到了这里,当我们做出了一些令人满意的效果后最重要的什么?当然是分享给好友!下面就让我们用xr-frame内置的分享系统来完成这个功能:

......
<xr-mesh node-id="mesh-plane" geometry="plane" material="mat" cube-shape="autoFit:true" bind:touch-shape="handleShare" />
......
handleShare: function() {
  this.scene.share.captureToFriends();
}

给识别后显示的视频Mesh加上了上面说过的shape绑定了触摸事件,然后在事件处理函数中直接用this.scene.share.captureToFriends()即可,效果如下:

当然,很多时候我们只是需要图片,然后用它接入微信的其他分享接口比如onShareAppMessage生命周期,此时使用share.captureToLocalPath接口即可,详细可见组件文档。

之后的,就交给你的创意

至此,我们简单体验了一下框架的各种能力,但主要是wxml为主,逻辑很少。对于入门的开发者,我们倾向于提供给开发者非常简单就能实现不错的效果,这也是渐进式开发的基础。更多详细的文档教程可见组件文档。

但除了这些简单的用法外,框架还提供了高度灵活的组件化特性。开发者可以按照自身需求,定制属于自己的组件、元素、所有的资源等等,甚至如果有需求,我们还可以开放底层的RenderGraph来定制渲染流程。详细的定制开发能力可见接下来各个部分的文档,我们都做了比较详细的说明和引导。

好,入门就到此为止了,技术始终只是一个工具,剩下的就交给身为创作者的你了!

体验

小程序【前端XR】可快速体验,还有更多的案例

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/712887.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ChatGLM6B LORA微调

ChatGLM6B&ChatGLM2-6B微调 目录 ChatGLM6B&ChatGLM2-6B微调 微调硬件需求 3.1. LoRA概述 3.2. LoRA微调ChatGLM步骤 3.2.1. 项目和环境搭建 3.2.2. 数据集处理 3.2.3. 微调 3.2.4. 推理 3.2.5. 完整过程 3.3. LoRA微调ChatGLM步骤-——huggingface PEFT 3.3.1. 项目和…

通过动态地址解决网络数据采集问题

动态地址的作用 说到Python网络爬虫&#xff0c;很多人都会遇到困难。最常见的就是爬取过程中IP地址被屏蔽。虽然大部分都是几个小时内自动解封的&#xff0c;但这对于分秒必争的python网络爬虫来说&#xff0c;是一个关键性的打击&#xff01;当一个爬虫被阻塞时&#xff0c;…

Qt项目环境构建

工欲善其事必先利其器&#xff0c;使用Qt来进行开发&#xff0c;得先配置好一个合适的环境。下面是我关于Qt项目环境构建的一些小结 Qt的项目构建主要依赖.pro文件&#xff08;和.pri文件&#xff1a;include包含文件&#xff0c;提供pro的复用性高的东西给多个项目包含) 所以新…

【企业架构框架】Mitre 架构联邦

定义&#xff1a; 架构联合是用于企业架构开发、维护和使用的框架&#xff0c;它对齐、定位和链接分离但相关的架构和架构信息&#xff0c;以向用户提供无缝的外观。 关键词&#xff1a; 企业架构&#xff0c;联邦架构&#xff0c;适合联邦&#xff0c;语义对齐&#xff0c;分层…

想要做好联络口译,需要掌握哪些技能?

据了解&#xff0c;联络口译员经常需要陪在外宾或者委托人身边出席各种各样的活动&#xff0c;除了翻译工作之外&#xff0c;有时还要兼任接待、导游等工作。那么&#xff0c;想要做好联络口译工作&#xff0c;需要掌握哪些技能&#xff1f; 首先&#xff0c;熟悉中外文化差异及…

在页面里面引入外部网页或者vue的其他页面组件

<div id"iframe-container"><iframe :src"iframeUrl" width"100vw" height"100vh" v-if"flagTwo"></iframe> </div>//可以是路径也可以是网络链接 data(){ return{iframeUrl :, }this.iframeUrl h…

从0到1精通自动化测试,pytest自动化测试框架,Hooks函数获取用例执行结果(二十三)

一、前言 pytest提供的很多钩子(Hooks)方法方便我们对测试用例框架进行二次开发&#xff0c;可以根据自己的需求进行改造 先学习下pytest_runtest_makereport这个钩子方法&#xff0c;可以更清晰的了解用例的执行过程&#xff0c;并获取到每个用例的执行结果 二、pytest_run…

【Part 1】现在去博物馆都预约不上了,黑产多少有点疯狂了

近几年不知道为啥&#xff0c;突然兴起博物馆热了&#xff0c;去某个城市&#xff0c;总想去当地的博物馆去打卡&#xff08;当然&#xff0c;可能重点还在打卡&#xff09;。但是周末去湖南省博物馆&#xff0c;发现免费的博物馆&#xff0c;现在已经预约不上了&#xff0c;最…

python面向对象编程学习1

python面向对象的基本要点&#xff0c; 类(Class): 用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。 使用 class 语句来创建一个新类&#xff0c;class 之后为类的名称并以冒号结尾&#xff1b; __init__()方…

lammps教程:固定边界丢失原子的几种解决办法

我是小马老师&#xff0c;本文介绍固定边界丢失原子的几种解决办法。 在lammps摩擦、润湿、冲击等模拟中&#xff0c;z方向多设为固定的f边界&#xff0c;如何设置不合理&#xff0c;会出现丢失原子的情况。 1.原因分析 以切削模拟为例&#xff0c;边界条件设为ppf&#xff…

PHP+MySQL:查询所有表格信息

效果 代码 $sql "SHOW TABLES"; $sql "SHOW TABLES"; $result DB_query($sql, $db); $myrow DB_fetch_array($result) echo <td> <a href" . $RootPath . /table_detial.php?table_name . $myrow[0] . "> . $myrow[0] . …

使用Secure CRT自动记录日志的方法

窍门一&#xff1a;自动记录系统日志 配置网络或者系统设备&#xff0c;日志记录必可不少。 养成日志记录的工作习惯在某些关键时候绝对能够帮你大忙。可以帮你记录设备的交互信息&#xff0c;方便后续问题追溯。 例如配置过程中出现故障&#xff0c;你需要自证清白&#xf…

MQ(消息中间件)概述及 RabbitMQ 的基本介绍

概述 消息中间件&#xff08;MQ&#xff09; 消息队列中间件是分布式系统中重要的组件&#xff0c;主要解决应用解耦&#xff0c;异步消息&#xff0c;流量削锋等问题&#xff0c;实现高性能&#xff0c;高可用&#xff0c;可伸缩和最终一致性架构。流量削锋 &#xff1a; 削减…

云原生之深入解析Hadoop如何在K8S环境中部署

一、准备工作 Hadoop 是 Apache 软件基金会下一个开源分布式计算平台&#xff0c;以 HDFS&#xff08;Hadoop Distributed File System&#xff09;、MapReduce&#xff08;Hadoop2.0 加入了 YARN&#xff0c;Yarn 是资源调度框架&#xff0c;能够细粒度的管理和调度任务&…

Java8中Lambda表达式和方法引用

一、Lambda表达式 1.1 函数式编程思想 1&#xff09;概念 ​ 面向对象思想需要关注用什么对象完成什么事情。而函数式编程思想就类似于我们数学中的函数。它主要关注的是对数据进行了什么操作。 2&#xff09;优点 代码简洁&#xff0c;开发快速接近自然语言&#xff0c;易…

1-n范围内的质数查找:埃拉托斯特尼筛法

文章目录 质数查找思路质数定义代码思路 写法重要逻辑&#xff1a;第一层for循环结束条件是i * i < n而不是i<n第二层循环如何筛查i所有倍数 完整版&#xff1a;返回0-n正整数中所有质数时间复杂度例题参考资料&#xff1a; 质数查找思路 质数定义 质数是一个自然数&am…

操作系统之调度算法总结

目录 一、进度调度算法 二、内存调度算法 一、先来先服务调度算法 二、最短寻道时间优先调度算法 三、扫描算法 四、循环扫描算法 五、LOOK和C-LOOK算法 三、内存页面置换算法 一、进度调度算法 常见的进度调度算法以及特点如下&#xff1a; 二、磁盘调度算法 一、先…

【大数据】初步认识StarRocks

StarRocks是什么 StarRocks 是新一代极速全场景 MPP 数据库。 StarRocks 充分吸收关系型 OLAP 数据库和分布式存储系统在大数据时代的优秀研究成果&#xff0c;在业界实践的基础上&#xff0c;进一步改进优化、升级架构&#xff0c;并增添了众多全新功能&#xff0c;形成了全新…

数据挖掘18大算法实现以及其他相关经典DM算法:决策分类,聚类,链接挖掘,关联挖掘,模式挖掘、图算法,搜索算法等

【机器学习入门与实践】入门必看系列&#xff0c;含数据挖掘项目实战&#xff1a;模型融合、特征优化、特征降维、探索性分析等&#xff0c;实战带你掌握机器学习数据挖掘 专栏详细介绍&#xff1a;【机器学习入门与实践】合集入门必看系列&#xff0c;含数据挖掘项目实战&…

TiDB(1):TiDB简介

1 从MySQL到TiDB 1.1 场景引入 假设现在有一个高速发展的互联网公司,核心业务库MySQL的数据量已经近亿行,且还在不断增长中,公司对于数据资产较为重视,所有数据要求多副本保存至少5年,且除了有对历史数据进行统计分析的离线报表业务外,还有一些针对用户数据实时查询的需求,如用…