使用 Three.js 和 TensorFlow.js 制作具有头部跟踪功能的交互式数字相框

2025-06-08

使用 Three.js 和 TensorFlow.js 制作具有头部跟踪功能的交互式数字相框

本文最初发布在我的博客上


过去几周,我一直在忙着一个新项目,想复制一种名为“头部耦合透视”的视觉效果。这项技术并不新鲜,但我对如何使用 Three.js 来实现它很感兴趣,这样我就能利用头部追踪技术制作一些互动艺术作品。

最终结果如下:

iPad 在网页上显示 3D 效果,盒子里放着一个花盆。视角会随着用户的位置而移动,给人一种仿佛在看盒子内部的感觉。

随着用户的移动,视角也会发生变化,给人一种能够看到框架内部的感觉,尽管这是一个 2D 显示。

图形是使用Three.js制作的,植物是从Sketchfab下载的 3D 模型,头部跟踪是使用TensorFlow.js中的MoveNet 模型完成的。

在对实现透视效果进行一些研究时,我了解到它与改变相机的投影矩阵有关,并偶然发现了对 Three.js repo 的拉取请求,这似乎接近我想要的。

PR 已合并,frameCorners()库中添加了一个名为 的新工具。根据文档,该工具“设置 PerspectiveCamera 的投影矩阵和四元数,以精确框住任意矩形的角”
这听起来正是我需要的!如果你仔细观察上面的演示,你会注意到,随着视角的变化,盒子的外角的位置并没有改变。

更新相机的投影矩阵

使用此实用程序的方法是将相机和 3 个向量传递给它,这 3 个向量表示代表任意矩形的点的坐标。

CameraUtils.frameCorners(
  camera,
  bottomLeftCorner,
  bottomRightCorner,
  topLeftCorner,
  false // This boolean is for the argument `estimateViewFrustum` but to be honest I don't quite understand what it means.
);
Enter fullscreen mode Exit fullscreen mode

在我的场景中,我使用一个平面几何体来创建 5 个网格,它们组成了我的“盒子”。这个几何体大约 100x100,每个网格都有不同的位置和旋转,具体取决于它位于盒子的哪一侧。

这里有一些代码示例来说明我所说的内容

// Top part of the box
planeTop.position.y = 100;
planeTop.rotateX(Math.PI / 2);

// bottom part of the box
planeBottom.rotateX(-Math.PI / 2);

// Back of the box
planeBack.position.z = -50;
planeBack.position.y = 50;

// Right side of the box
planeRight.position.x = 50;
planeRight.position.y = 50;
planeRight.rotateY(-Math.PI / 2);

// Left side of the box
planeLeft.position.x = -50;
planeLeft.position.y = 50;
planeLeft.rotateY(Math.PI / 2);
Enter fullscreen mode Exit fullscreen mode

考虑到这些位置,我们可以创建向量来表示我们想要用于相机的点:

let bottomLeftCorner = new THREE.Vector3();
let bottomRightCorner = new THREE.Vector3();
let topLeftCorner = new THREE.Vector3();

bottomLeftCorner.set(-50.0, 0.0, -20.0);
bottomRightCorner.set(50.0, 0.0, -20.0);
topLeftCorner.set(-50.0, 100.0, -20.0);
Enter fullscreen mode Exit fullscreen mode

bottomLeftCorner向量具有x-50 的位置以匹配x的坐标planeLefty位置为 0 以匹配 y 位置(planeBottom默认值为 0),并且z位置为 -20 以具有一点深度但不会太多。

我花了一些时间来了解如何选择矢量的坐标以获得我想要的效果,但这个 GIF很有帮助:

GIF 展示了如何通过改变矢量的位置来修改相机的位置和方向,以使其始终与任意矩形的坐标相匹配

当您改变矢量的坐标时,相机会改变位置和方向来构图这些角。

但这只是解决方案的一部分,第二部分有点意外发生。😂

轨道控制

一旦我设法获得矢量的正确坐标并使用frameCorners()实用程序,相机的位置就会适合正确的矩形,但是当尝试通过面部跟踪改变视角时,就会发生一些奇怪的事情。

我希望我当时能把它记录下来,这样我就可以告诉你我的意思,但无论如何我都会尝试解释它。

在本文开头的演示中,你可以看到,无论视角如何变化,背平面始终与我平行。当我只使用frameCorners()这个平面时,它一直在旋转,所以向量的z位置也在变化,这完全没有呈现出真实的效果。

有点像下面的 GIF,但想象一下它只发生在一侧:

显示推拉变焦效果的 GIF

今天我了解到这被称为“移动变焦”!

为了尝试调试它,我认为使用 OrbitControls 可能会有所帮助,让我围绕场景旋转,也许使用相机助手来查看发生了什么,但相反,它只是解决了我的问题!

只需添加let cameraControls = new OrbitControls(camera, renderer.domElement);,我现在就可以改变场景的视角而无需旋转背板,这使它看起来更加逼真!

接下来发生的事情纯粹是因为懒惰......我本可以更深入地了解 OrbitControls 的工作原理,以准确找出我需要的部分,但是为了节省一些时间(毕竟这只是一个附带项目),我直接对OrbitControls.js文件进行了一些更新。

我找到了这个函数的位置handleMouseMoveRotate,复制了它,并调用了新的函数handleFaceMoveRotate来处理面部运动。我稍微修改了一下,接收的是面部坐标而不是鼠标坐标,TADAAA!!成功了!🎉

后续步骤Next steps

我想创建更多的场景,并且我有一个想法可以推动这个项目进一步发展,但我觉得现在我需要休息一下。

当我花太多时间调试一个业余项目时,有时会失去乐趣。我需要把它放一边,等我感到兴奋的时候再回来继续做。😊

与此同时,请随意检查代码

链接:https://dev.to/devdevcharlie/making-an-interactive-digital-frame-with-head-tracking-using-three-js-and-tensorflow-js-5dfo
PREV
使用 JavaScript 和 Web 套接字制作交互式悬浮滑板游戏。
NEXT
使用 JavaScript AWS Security LIVE 升级!