Movatterモバイル変換


[0]ホーム

URL:


开发
中文
EN
取消

# 开始

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

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

本文只是开始指南,更加详细的信息请见组件框架文档API文档

⚠️xr-frame在基础库v2.32.0开始基本稳定,发布为正式版,但仍有一些功能还在开发,请见限制和展望

# 新建一个XR组件

首先创建项目,让我们选择小程序工程:

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

index.json:

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

index.wxml:

<xr-scene><xr-cameraclear-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-startdisable-scrollid="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-meshnode-id="cube"geometry="cube"/><xr-cameraclear-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-materialasset-id="simple"effect="simple"uniforms="u_baseColorFactor:0.8 0.4 0.4 1"/><xr-meshnode-id="cube"geometry="cube"material="simple"/>

效果如下:

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

<xr-lighttype="ambient"color="1 1 1"intensity="1"/><xr-lighttype="directional"rotation="40 70 0"color="1 1 1"intensity="3"cast-shadow/><xr-meshnode-id="cube"cast-shadowgeometry="cube"uniforms="u_baseColorFactor:0.8 0.4 0.4 1"/><xr-meshposition="0 -1 0"scale="4 1 4"receive-shadowgeometry="plane"uniforms="u_baseColorFactor:0.4 0.6 0.8 1"/>

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

# 有点寡淡,加上图像

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

<xr-assetsbind:progress="handleAssetsProgress"bind:loaded="handleAssetsLoaded"><xr-asset-loadtype="texture"asset-id="waifu"src="https://mmbizwxaminiprogram-1258344707.cos.ap-guangzhou.myqcloud.com/xr-frame/demo/waifu.png"/></xr-assets><xr-meshnode-id="cube"cast-shadowgeometry="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-meshposition="0 -1 0"scale="4 1 4"receive-shadowgeometry="plane"uniforms="u_baseColorMap: {{avatarTextureId}}"/>

效果如下:

# 让场景更丰富,环境数据

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

<xr-envenv-data="xr-frame-team-workspace-day"/><xr-meshnode-id="cube"cast-shadowgeometry="cube"uniforms="u_baseColorMap: waifu,u_metallicRoughnessValues:1 0.1"/><xr-cameraposition="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-loadtype="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-envenv-data="xr-frame-team-workspace-day"sky-map="video-office"/>

效果如下:

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

<xr-asset-loadtype="texture"asset-id="weakme"src="https://mmbizwxaminiprogram-1258344707.cos.ap-guangzhou.myqcloud.com/xr-frame/demo/weakme.jpg"/><xr-envenv-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-loadasset-id="anim"type="keyframe"src="/assets/animation.json"/><xr-meshnode-id="cube"cast-shadowanim-keyframe="anim"anim-autoplay="clip:cube,speed:2"geometry="cube"uniforms="u_baseColorMap: waifu,u_metallicRoughnessValues:1 0.1"/><xr-meshnode-id="plane"position="0 -1 0"scale="4 1 4"receive-shadowanim-keyframe="anim"anim-autoplay="clip:plane"geometry="plane"uniforms="u_baseColorMap: {{avatarTextureId}}"/><xr-cameraposition="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-loadtype="gltf"asset-id="damage-helmet"src="https://mmbizwxaminiprogram-1258344707.cos.ap-guangzhou.myqcloud.com/xr-frame/demo/damage-helmet/index.glb"/><xr-asset-loadtype="gltf"asset-id="miku"src="https://mmbizwxaminiprogram-1258344707.cos.ap-guangzhou.myqcloud.com/xr-frame/demo/miku.glb"/><xr-gltfnode-id="damage-helmet"model="damage-helmet"/><xr-gltfmodel="miku"position="-0.15 0.75 0"scale="0.07 0.07 0.07"rotation="0 180 0"anim-autoplay/><xr-cameraposition="0 1.5 4"target="damage-helmet"background="skybox"clear-color="0.4 0.8 0.6 1"camera-orbit-control/>

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

# 再来点交互

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

<xr-gltfnode-id="damage-helmet"model="damage-helmet"id="helmet"mesh-shapebind:touch-shape="handleTouchModel"/><xr-gltfmodel="miku"position="-0.15 0.75 0"scale="0.07 0.07 0.07"rotation="0 180 0"anim-autoplayid="miku"cube-shape="autoFit:true"shape-gizmobind: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-scenebind:ready="handleReady"bind:tick="handleTick">......<xr-gltfnode-id="damage-helmet"model="damage-helmet"id="helmet"mesh-shapebind:touch-shape="handleTouchModel"/><xr-gltfmodel="miku"position="-0.15 0.75 0"scale="0.07 0.07 0.07"rotation="0 180 0"anim-autoplayid="miku"cube-shape="autoFit:true"shape-gizmobind:touch-shape="handleTouchModel"/><xr-cameraid="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-startdisable-scrollid="main-frame"width="{{renderWidth}}"height="{{renderHeight}}"style="width:{{width}}px;height:{{height}}px;"bind:assetsLoaded="handleLoaded"bind:syncPositions="handleSyncPositions"/><blockwx:if="{{loaded}}"wx:for="{{positions}}"wx:for-item="pos"wx:key="*this"><viewstyle="display: block; position: absolute;left: {{pos[0]}}px;top: {{pos[1]}}px;background: {{pos[2]}};transform: translate(-50%, -50%);"><viewstyle="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-scenear-system="modes:Plane"bind:ready="handleReady"><xr-assetsbind:loaded="handleAssetsLoaded"><xr-asset-loadtype="gltf"asset-id="anchor"src="https://mmbizwxaminiprogram-1258344707.cos.ap-guangzhou.myqcloud.com/xr-frame/demo/ar-plane-marker.glb"/><xr-asset-loadtype="gltf"asset-id="miku"src="https://mmbizwxaminiprogram-1258344707.cos.ap-guangzhou.myqcloud.com/xr-frame/demo/miku.glb"/></xr-assets><xr-envenv-data="xr-frame-team-workspace-day"/><xr-lighttype="ambient"color="1 1 1"intensity="1"/><xr-lighttype="directional"rotation="40 70 0"color="1 1 1"intensity="3"cast-shadow/><xr-ar-trackermode="Plane"><xr-gltfmodel="anchor"></xr-gltf></xr-ar-tracker><xr-nodenode-id="setitem"visible="false"><xr-gltfmodel="miku"anim-autoplayscale="0.08 0.08 0.08"rotation="0 180 0"/></xr-node><xr-cameraclear-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系统后,我们便可以尝试更多不同的模式来玩做一些好玩的效果。接下来的是人脸识别模式,为此我们只需要在上面的代码中改几句,就可以给自己带上Joker的面具(逃):

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

<xr-scenear-system="modes:Face;camera:Front"bind:ready="handleReady"bind:tick="handleTick"><xr-assetsbind:loaded="handleAssetsLoaded"><xr-asset-loadtype="gltf"asset-id="mask"src="https://mmbizwxaminiprogram-1258344707.cos.ap-guangzhou.myqcloud.com/xr-frame/demo/jokers_mask_persona5.glb"/></xr-assets><xr-envenv-data="xr-frame-team-workspace-day"/><xr-lighttype="ambient"color="1 1 1"intensity="1"/><xr-lighttype="directional"rotation="40 70 0"color="1 1 1"intensity="3"/><xr-ar-trackermode="Face"auto-sync="43"><xr-gltfmodel="mask"rotation="0 180 0"scale="0.5 0.5 0.5"/></xr-ar-tracker><xr-cameraclear-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-scenear-system="modes:Hand"bind:ready="handleReady"bind:tick="handleTick"><xr-assetsbind:loaded="handleAssetsLoaded"><xr-asset-loadtype="gltf"asset-id="cool-star"src="https://mmbizwxaminiprogram-1258344707.cos.ap-guangzhou.myqcloud.com/xr-frame/demo/cool-star.glb"/></xr-assets><xr-envenv-data="xr-frame-team-workspace-day"/><xr-lighttype="ambient"color="1 1 1"intensity="1"/><xr-lighttype="directional"rotation="40 70 0"color="1 1 1"intensity="3"cast-shadow/><xr-ar-trackerid="tracker"mode="Hand"auto-sync="4"><xr-gltfmodel="cool-star"anim-autoplay/></xr-ar-tracker><xr-cameraclear-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-scenear-system="modes:OSD"bind:ready="handleReady"><xr-assetsbind:loaded="handleAssetsLoaded"><xr-asset-materialasset-id="mat"effect="simple"uniforms="u_baseColorFactor: 0.8 0.6 0.4 0.7"states="alphaMode:BLEND"/></xr-assets><xr-node><xr-ar-trackermode="OSD"src="https://mmbizwxaminiprogram-1258344707.cos.ap-guangzhou.myqcloud.com/xr-frame/demo/marker/osdmarker-test.jpg"><xr-meshgeometry="plane"material="mat"rotation="-90 0 0"/></xr-ar-tracker></xr-node><xr-cameraclear-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-scenear-system="modes:Marker"bind:ready="handleReady"><xr-assetsbind:loaded="handleAssetsLoaded"><xr-asset-loadtype="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-materialasset-id="mat"effect="simple"uniforms="u_baseColorMap: video-hikari"/></xr-assets><xr-nodewx:if="{{loaded}}"><xr-ar-trackermode="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-meshnode-id="mesh-plane"geometry="plane"material="mat"/></xr-ar-tracker></xr-node><xr-cameraclear-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-loadtype="texture"asset-id="point"src="https://mmbizwxaminiprogram-1258344707.cos.ap-guangzhou.myqcloud.com/xr-frame/demo/particles/point.png"/>  ......<xr-nodewx:if="{{loaded}}"><xr-ar-trackermode="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-meshnode-id="mesh-plane"geometry="plane"material="mat"/><xr-particlecapacity="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-loadasset-id="anim"type="keyframe"src="/assets/animation.json"/>  ......<xr-asset-post-processasset-id="vignette"type="vignette"data="intensity:1,smoothness:4,color:1 0 0 1"anim-keyframe="anim"anim-autoplay/><xr-cameraclear-color="0.4 0.8 0.6 1"background="ar"is-ar-camerapost-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-meshnode-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来定制渲染流程。详细的定制开发能力可见接下来各个部分的文档,我们都做了比较详细的说明和引导。

好,入门就到此为止了,技术始终只是一个工具,剩下的就交给身为创作者的你了!在此之前,不如先看看这些DEMO吧:

The translations are provided by WeChat Translation and are for reference only. In case of any inconsistency and discrepancy between the Chinese version and the English version, the Chinese version shall prevail.Incorrect translation.Tap to report.
反馈

[8]ページ先頭

©2009-2025 Movatter.jp