抖音开放平台Logo
开发者文档
“/”唤起搜索
控制台

AI/AR:平面追踪(基于 three.js)

收藏
我的收藏

准备工作

在开始阅读本教程之前,请先阅读 AI/AR:足部识别(基于 three.js 和 microapp-ar-three)到 “将相机画在 Canvas 上” 这一节,本教程将从这里开始。
由于本文通过 OBJLoader 加载了 obj 模型,所以推荐使用 microapp-ar-three 中解决了兼容性问题的 three.js,在终端执行 npm i @douyin-microapp/microapp-ar-three 之后,可以在以下路径中找到:node_modules/@douyin-microapp/microapp-ar-three/libs/three.js。Slam AR 需要在抖音 1860(基础库 2.32.0)版本以上运行。

获取算法数据

AI/AR:足部识别(基于 three.js 和 microapp-ar-three)中我们了解到了如何绘制 AI/AR 场景中的相机,以及如何初始化 AlgorithmManager。接下来我们基于“将相机画在 canvas 上” 这一节的示例,获取 slam AR 的算法数据。
由于 slam AR 算法需要使用与相机画面相关的时间戳,所以我们需要通过 cameraData.timestamp 来获取这个时间戳:
export function onFrame(cameraData) { if (algorithmManager != null) { algorithmManager.doExecute({ input: cameraData, width: width, height: height, timeStamp: cameraData.timestamp / 1e9, success: (algMgr) => { cb(algMgr); }, fail: (errMsg) => { console.log(errMsg); }, }); } }
cameraData.timestamp 的精度是纳秒,算法接收的时间戳单位是秒,所以需要做一次转换。
尝试输出一下获取到的算法结果,扫码运行后打开调试模式,可以看到如下输出:

绘制立方体

我们先使用 three.js 绘制一个 box 模型:
let cube = null; export function initModule(_canvas) { ... const geometry = new THREE.BoxGeometry(5, 5, 5); const material = new THREE.MeshNormalMaterial(); cube = new THREE.Mesh(geometry, material); cube.position.z = -20 ... }
算法结果返回的 view 和 projection 矩阵分别用于更新 three.js 相机的观察矩阵和投影矩阵:
function updateView(camera, matrix) { let position = new THREE.Vector3(); let quaternion = new THREE.Quaternion(); let scale = new THREE.Vector3(1, 1, 1); matrix.decompose(position, quaternion, scale); position.x = position.x * 100; position.y = position.y * 100; position.z = position.z * 100; camera.position.copy(position); camera.scale.copy(scale); camera.quaternion.copy(quaternion); }
运行小程序,将相机对准平面,可看到对正方体模型追踪的效果:
完整的项目工程可在开发者工具中打开

绘制模型

有时候需要加载模型作为平面追踪的对象,本文用 logo.obj 这个 OBJ 模型作为示例。和之前的模型文件一样,我们通过上面的链接让小程序获取该模型,在 RenderModule.js 中添加以下代码:
const modelUrl = "https://lf3-developer.bytemastatic.com/obj/developer/misc/AI_AR_demo/logo.obj"; let slamModel = null; function loadOBJModel(modelUrl) { const loader = new THREE.OBJLoader(); loader.load( modelUrl, function (loadedMesh) { tt.showToast({ title: "模型加载成功", duration: 2000, success(res) {}, fail(res) { console.log(`showToast failed`); }, }); slamModel = loadedMesh; const cubeMaterial = new THREE.MeshLambertMaterial({ color: "white" }); slamModel.children.forEach(function (child) { child.material = cubeMaterial; }); slamModel.scale.set(100, 100, 100); mixedScene.add(slamModel); var modelLight = new THREE.HemisphereLight(0xffffff, 0x444444); modelLight.position.set(0, 20, 0); mixedScene.add(modelLight); }, undefined, function (e) { tt.showToast({ title: "模型加载失败", duration: 2000, success(res) { console.log(`${res}`); }, fail(res) { console.log(`showToast failed`); }, }); console.error("load model error :: ", e); } ); } export function initModule(_canvas) { loadOBJModel(modelUrl); }
运行小程序就可以看到效果:
完整的项目工程可在开发者工具中打开