背景
某天需求方扔了个链接:【原神官方】AR 立牌,询问小程序是否能够实现类似的AR效果。
作为打工的人的我,于是被迫补起:早已过了风口(bushi)的AR知识。
调研
实体 AR 立牌
斥巨资在闲鱼淘了个多莉的角色立牌,实际体验了下完整的交互流程,AR的氛围感还是不错的。
AR在线制作平台
KIVICUBE 提供了可视化的3D场景搭建和动画编排系统:零基础也可创造和发布酷炫的AR作品。
官方同时提供了大量创意模版供大家一键使用
技术方案
在微信小程序的技术体系下,官方提供了两种解决方案:
小程序也在基础库 2.20.0 版本开始提供了开发 AR 功能的能力,即 VisionKit。VisionKit 包含了 AR 在内的视觉算法
xr-frame是一套小程序官方提供的XR/3D应用解决方案,基于混合方案实现,性能逼近原生、效果好、易用、强扩展、渐进式、遵循小程序开发标准
简单来说:
- VisionKit 是底层视觉引擎,提供能力但需要自己拼装。
- XR-FRAME 是完整开发框架,整合了渲染、交互与视觉能力,适合快速实现产品化的 AR 效果。
虽然理论上我们完全可以基于 VisionKit 并结合 Three.js 或自研的 WebGL 渲染逻辑来实现 AR 效果,但这种方案开发成本较高:
开发者需要自行处理相机帧与渲染管线的衔接、追踪状态管理、姿态矩阵更新等底层逻辑,整体工程量大且维护复杂。(Kivicube 正是使用此方案,确保自研的 AR 可视化搭建平台的产物能兼容各大平台:微信小程序、原生网页H5)
而 XR-FRAME 在此基础上进一步封装了完整的 3D 渲染与交互体系,提供了大量开箱即用的能力,让开发者可以像写前端组件一样,以声明式方式快速构建 AR 场景,而不必深入到底层渲染细节。
因此最后决定:先用 XR-FRAME 快速交付第一版 MVP 小程序
XR-FRAME 指南
由于是小程序开发,所以学习 XR-FRAME 的前提,需要有小程序基础知识
而官方的两个在线文档,内容还是略显单薄,强烈建议直接参考官方 《xr-frame系统的示例集》 的源码进行实战学习
本教程配套代码:xr-frame-tutorial
XR 组件 模版代码
创建 XR 组件
1{ 2 "component": true, 3 "usingComponents": {}, 4 "renderer": "xr-frame" 5} 6
1<xr-scene> 2 <xr-camera clear-color="0.4 0.8 0.6 1" /> 3</xr-scene> 4
使用 XR 组件
1{ 2 "usingComponents": { 3 "demo1": "../../components/demo1" 4 }, 5 "disableScroll": true, 6 "renderer": "webview" 7} 8
1<view> 2 <demo1 3 disable-scroll 4 id="main-frame" 5 width="{{renderWidth}}" 6 height="{{renderHeight}}" 7 style="width:{{width}}px;height:{{height}}px;top:{{top}}px;left:{{left}}px;" 8 /> 9</view> 10
1Page({ 2 data: { 3 left: 0, 4 top: 0, 5 width: 0, 6 height: 0, 7 renderWidth: 0, 8 renderHeight: 0, 9 }, 10 onLoad(options) { 11 const info = wx.getSystemInfoSync() 12 const width = info.windowWidth 13 const height = info.windowHeight 14 const dpi = info.pixelRatio 15 16 this.setData({ 17 width, 18 height, 19 renderWidth: width * dpi, 20 renderHeight: height * dpi 21 }); 22 }, 23}) 24
渲染 XR 组件
渲染效果:绿色背景的画布 Canvas
3D 渲染
源码示例: 3D 渲染
最基础的 3D 渲染 思维导图
添加物体
添加一个 立方体 (Cube)物体
1<xr-scene> 2 <xr-mesh node-id="cube" geometry="cube" /> 3 <!-- 设置 target 指向 cube,让相机始终看向这个立方体--> 4 <xr-camera clear-color="0.4 0.8 0.6 1" position="1 3 4" target="cube" camera-orbit-control /> 5</xr-scene> 6
物体黑色是因为在我们没有给xr-mesh指定材质时,用的是基于PBR效果的默认材质,需要光照
临时解决方案: 创建一个不需要光照的材质,并且使用这个材质
1<xr-scene> 2 <xr-asset-material asset-id="simple" effect="simple" uniforms="u_baseColorFactor:0.8 0.4 0.4 1" /> 3 <xr-mesh node-id="cube" geometry="cube" material="simple" /> 4 <xr-camera clear-color="0.4 0.8 0.6 1" position="1 3 4" target="cube" camera-orbit-control /> 5</xr-scene> 6
添加光照
1<xr-scene> 2 <xr-mesh node-id="cube" geometry="cube" uniforms="u_baseColorFactor:0.8 0.4 0.4 1" /> 3 <!-- 一个环境光 --> 4 <xr-light type="ambient" color="1 1 1" intensity="1" /> 5 <!-- 一个主平行光 --> 6 <xr-light type="directional" rotation="40 70 0" color="1 1 1" intensity="3" cast-shadow /> 7 <xr-camera clear-color="0.4 0.8 0.6 1" position="1 3 4" target="cube" camera-orbit-control /> 8</xr-scene> 9
添加纹理
纹理 Texture 是 GPU 中的图像,供着色器采样使用。在框架中其一般被作为材质的一部分uniforms使用
1<xr-scene> 2 <xr-assets bind:progress="handleAssetsProgress" bind:loaded="handleAssetsLoaded"> 3 <!-- 加载纹理 --> 4 <xr-asset-load type="texture" asset-id="waifu" src="https://mmbizwxaminiprogram-1258344707.cos.ap-guangzhou.myqcloud.com/xr-frame/demo/waifu.png" /> 5 <!-- 材质中使用纹理 --> 6 <xr-asset-material asset-id="mat" uniforms="u_baseColorMap: waifu" /> 7 </xr-assets> 8 9 <!-- 使用材质 --> 10 <xr-mesh node-id="cube" geometry="cube" material="mat" cast-shadow /> 11 12 <!-- 一个环境光 --> 13 <xr-light type="ambient" color="1 1 1" intensity="1" /> 14 <!-- 一个主平行光 --> 15 <xr-light type="directional" rotation="40 70 0" color="1 1 1" intensity="3" cast-shadow /> 16 <xr-camera clear-color="0.4 0.8 0.6 1" position="1 3 4" target="cube" camera-orbit-control /> 17</xr-scene> 18
我们继续新加一个 平面 (Plane) 物体
1<xr-assets bind:progress="handleAssetsProgress" bind:loaded="handleAssetsLoaded"> 2 <xr-asset-load type="texture" asset-id="deepred" src="https://avatars.githubusercontent.com/u/13102815" /> 3 <xr-asset-material asset-id="mat2" uniforms="u_baseColorMap: deepred" /> 4</xr-assets> 5 6<xr-mesh node-id="plane" geometry="plane" material="mat2" position="0 1 0" /> 7
添加动画
1{ 2 "setting": { 3 "ignoreDevUnusedFiles": false, 4 "ignoreUploadUnusedFiles": false 5 } 6} 7
添加一个无限旋转的动画
- keyframe 中定义了帧动画具体的行为,本质上类似于 CSS 中的 keyframes
- animation 中定义了帧动画的播放配置,本质上类似于 CSS 中的 animation
注意:旋转的值是弧度!!!
360度等于2π弧度,约等于 2 * 3.14
1{ 2 "keyframe": { 3 "logo": { 4 "0": { 5 "rotation": [0, 0, 0] 6 }, 7 "100": { 8 // 注意是弧度,360度等于2π,约等于6.28 9 "rotation": [6.28, 0, 0] 10 } 11 } 12 }, 13 "animation": { 14 "logo": { 15 "keyframe": "logo", 16 "duration": 4, 17 "ease": "linear", 18 "loop": -1 19 } 20 } 21} 22
1<!-- 加载动画资源 --> 2<xr-asset-load asset-id="anim" type="keyframe" src="/assets/animation.json"/> 3 4<xr-mesh node-id="plane" geometry="plane" material="mat2" position="0 1 0" anim-keyframe="anim" anim-autoplay="clip:logo" /> 5
添加 3D 模型
支持 GLTF 格式模型
1<!-- 加载 GLTF 模型 --> 2<xr-asset-load type="gltf" asset-id="miku" src="https://mmbizwxaminiprogram-1258344707.cos.ap-guangzhou.myqcloud.com/xr-frame/demo/miku.glb" /> 3 4<!-- 渲染 GLTF 组件 --> 5<xr-gltf model="miku" position="-0.15 0.75 1" scale="0.07 0.07 0.07" rotation="0 180 0" anim-autoplay /> 6
AR 系统
AR 能力,需要调用摄像头权限
微信开发者工具无法模拟,请使用真机预览模式
Plane 识别
源码示例: Plane 模式
Plane 模式是 AR 中的平面检测与追踪功能
常见的交互流程:
- 扫描阶段
- 用户移动手机摄像头
- AR 系统分析画面,识别水平或垂直的平面
- 常见平面:地板、桌面、墙壁、床面等
- 检测阶段
- 找到平面后,显示视觉指示器(视频中的蓝色圆环)
- 指示器跟随检测到的平面移动
- 放置阶段
- 用户点击屏幕
- 虚拟物体被"放置"到现实世界的平面上
1<!-- AR system 系统开启 Plane 模式 --> 2<xr-scene ar-system="modes:Plane" bind:ready="handleReady"> 3 <xr-assets bind:loaded="handleAssetsLoaded"> 4 <xr-asset-load type="gltf" asset-id="anchor" src="https://mmbizwxaminiprogram-1258344707.cos.ap-guangzhou.myqcloud.com/xr-frame/demo/ar-plane-marker.glb" /> 5 <xr-asset-load type="gltf" asset-id="miku" src="https://mmbizwxaminiprogram-1258344707.cos.ap-guangzhou.myqcloud.com/xr-frame/demo/miku.glb" /> 6 <xr-asset-load type="texture" asset-id="deepred" src="https://avatars.githubusercontent.com/u/13102815" /> 7 <xr-asset-material asset-id="mat" uniforms="u_baseColorMap: deepred" /> 8 </xr-assets> 9 <xr-light type="ambient" color="1 1 1" intensity="1" /> 10 <xr-light type="directional" rotation="40 70 0" color="1 1 1" intensity="3" cast-shadow /> 11 <!-- AR 追踪器 对应也要开启 Plane 模式 --> 12 <xr-ar-tracker mode="Plane"> 13 <!-- 找到平面后,显示视觉指示器 --> 14 <xr-gltf model="anchor"></xr-gltf> 15 </xr-ar-tracker> 16 <!-- 首先隐藏虚拟物体 --> 17 <xr-node node-id="setitem" visible="false"> 18 <xr-gltf model="miku" anim-autoplay scale="0.07 0.07 0.07" rotation="0 180 0" /> 19 <xr-mesh node-id="plane" geometry="plane" material="mat" scale="0.3 0.3 0.3" /> 20 </xr-node> 21 <!-- camera 的 background 设置成 ar --> 22 <xr-camera clear-color="0.4 0.8 0.6 1" background="ar" is-ar-camera /> 23</xr-scene> 24
1Component({ 2 methods: { 3 handleAssetsLoaded: function ({ detail }) { 4 wx.showToast({ title: '点击屏幕放置模型' }); 5 this.scene.event.add('touchstart', () => { 6 // 虚拟物体被"放置"到现实世界的平面 7 this.scene.ar.placeHere('setitem', true); 8 }); 9 }, 10 handleReady: function ({ detail }) { 11 this.scene = detail.value; 12 }, 13 } 14}) 15
Marker 识别
源码示例: Marker 模式
Marker 模式,能够识别预先设定的目标物体(定义为 Marker,包括2D平面物体和3D物体),进行视觉跟踪与定位,通过在目标物体周围渲染虚拟物体,从而实现AR功能。
模型与现实物体保持空间位置关系(即:3D 层级 效果)。
Marker 分类
- 2D Marker,仅适用于平面类物体,用户上传一张平面物体的俯视图像作为目标物体,算法运行时识别该平面物品,并渲染出相关虚拟物体。2D Marker可以理解为特殊的 3D Marker。
- 3D Marker,相比于2D Marker,能够识别3D物体,不局限于平面物体,具有更广的使用范围,算法运行前,需要手动制作3D Marker的识别目标文件(.map文件),然后算法运行时载入该文件用于识别
1<xr-scene ar-system="modes:Marker" bind:ready="handleReady"> 2 <xr-assets bind:loaded="handleAssetsLoaded"> 3 <xr-asset-load type="video-texture" asset-id="hikari" options="loop:true" src="https://assets.xxxx.com/resources/cdn/20250925/1c85f5b5ecde29ea.mp4" /> 4 <xr-asset-load type="texture" asset-id="deepred2" src="https://assets.xxxx.com/resources/cdn/20250925/c737cf37d083d543.png" /> 5 <xr-asset-material asset-id="mat" effect="simple" uniforms="u_baseColorMap: video-hikari" /> 6 <xr-asset-material asset-id="mat3" effect="simple" uniforms="u_baseColorMap: deepred2" /> 7 </xr-assets> 8 <xr-node wx:if="{{loaded}}"> 9 <xr-ar-tracker mode="Marker" bind:ar-tracker-switch="handleTrackerSwitch" src="https://assets.xxxx.com/resources/cdn/20250925/fe8dad0e9800ef81.jpg"> 10 <xr-mesh node-id="mesh-plane" geometry="plane" material="mat" scale="1.7778 1 1" position="0 0.1 0" /> 11 <xr-mesh node-id="plane" geometry="plane" material="mat3" position="0.1 0.8 0.1" scale="0.12 0.12 0.12" /> 12 </xr-ar-tracker> 13 </xr-node> 14 <xr-camera clear-color="0.4 0.8 0.6 1" background="ar" is-ar-camera /> 15</xr-scene> 16
1Component({ 2 methods: { 3 handleReady: function ({ detail }) { 4 this.scene = detail.value; 5 }, 6 handleAssetsLoaded: function ({ detail }) { 7 this.setData({ loaded: true }); 8 }, 9 handleTrackerSwitch: function ({ detail }) { 10 // 获取追踪状态 11 const active = detail.value; 12 const video = this.scene.assets.getAsset('video-texture', 'hikari'); 13 // 识别到物体,播放视频 14 // 识别中,暂停视频 15 active ? video.play() : video.stop(); 16 } 17 } 18}) 19
OSD 识别
源码示例: OSD 模式
OSD(One-shot Detection)Marker 识别模式,可以看成是一种特殊的 Marker 模式 :在摄像头画面上“贴图叠加”的模式,主要以 屏幕坐标系(2D) 为主,不真正感知空间深度
1<xr-scene ar-system="modes:OSD" bind:ready="handleReady"> 2 <xr-assets bind:loaded="handleAssetsLoaded"> 3 <xr-asset-material asset-id="mat" effect="simple" states="alphaMode:BLEND" /> 4 <xr-asset-material asset-id="text-simple" effect="simple" /> 5 </xr-assets> 6 <xr-node> 7 <xr-ar-tracker mode="OSD" src="https://assets.xxxx.com/resources/cdn/20250925/fe8dad0e9800ef81.jpg"> 8 <xr-node rotation="0 180 0"> 9 <xr-mesh node-id="text-wrap" geometry="plane" material="mat" position="0 1 0" scale="1 1 0.5" rotation="90 0 0" uniforms="u_baseColorFactor:0.875 0.616 0.624 1" /> 10 <xr-text node-id="text-name" position="-0.25 1.05 0" scale="0.1 0.1 0.1" material="text-simple" value="美食家蜜蜂" /> 11 </xr-node> 12 </xr-ar-tracker> 13 </xr-node> 14 <xr-camera clear-color="0.4 0.8 0.6 1" background="ar" is-ar-camera /> 15</xr-scene> 16
人脸识别
源码示例: 人脸识别
1<!-- AR system 系统开启 Face 模式 同时开启前置摄像头 --> 2<xr-scene ar-system="modes:Face;camera:Front"> 3 <xr-assets> 4 <xr-asset-load type="gltf" asset-id="mask" src="https://mmbizwxaminiprogram-1258344707.cos.ap-guangzhou.myqcloud.com/xr-frame/demo/jokers_mask_persona5.glb" /> 5 </xr-assets> 6 <!-- AR 追踪器 对应也要开启 Face 模式 --> 7 <!-- auto-sync 是一个数字数组,用于将对应顺序的子节点绑定到某个特征点上 --> 8 <xr-ar-tracker mode="Face" auto-sync="43"> 9 <!-- 包含识别成功后需要展示的场景 --> 10 <xr-gltf model="mask" rotation="0 180 0" scale="0.5 0.5 0.5" /> 11 </xr-ar-tracker> 12 <xr-light type="ambient" color="1 1 1" intensity="1" /> 13 <xr-light type="directional" rotation="40 70 0" color="1 1 1" intensity="3" /> 14 <!-- AR 相机 --> 15 <xr-camera background="ar" is-ar-camera /> 16</xr-scene> 17
除了人脸识别(Face),还有 人体识别(Body),人手识别(Hand),可以参考官方文档学习
总结
到这里,我们已经把 XR-FRAME 的基础能力都过了一遍:从创建场景、添加物体、设置光照,到加载模型和动画,再到四种 AR 识别模式(Plane、Marker、OSD、Face)。理论上,你已经可以做出一个简单的 AR 效果了。
但理论和实际总是有差距的。在下一篇实战篇中,我会分享更高阶的知识点和实际项目中的经验:
- Shader 着色器:实现自定义视觉效果,让 AR 场景更炫酷
- 同层渲染:XR 组件和小程序原生组件互相通信,实现复杂的 UI 交互
- 企业级方案:性能优化、状态管理、多设备兼容等实战经验
《玩转小程序AR-实战篇》 是转载文章,点击查看原文。
