【问题标题】:Efficient way to translate a camera dolly翻译相机小车的有效方法
【发布时间】:2025-12-16 08:15:02
【问题描述】:

我目前在小车上安装了一个 VR 摄像头,以便进行平移和旋转。 我正在尝试根据游戏手柄输入相对于相机的方向(与 VR 耳机链接)来翻译小车。

我还试图避免让小车相对于相机上下倾斜。

我当前的代码如下所示:

this.camerDirectionVector = new THREE.Vector3()
this.camera.getWorldDirection(this.cameraDirectionVector)
this.moveVec.y = 0
this.dolly.translateOnAxis(this.cameraDirectionVector, this.gamepad.axes[0] * this.moveSpeed)

这非常适合在相机指向的方向移动小车(减去 y 旋转)。

我想不通的是如何根据额外的游戏手柄输入相对于相机“左右”平移小车。

【问题讨论】:

  • 我读这篇文章的方式是你的 VR 用户被绑定到一个小车上,你想通过一个控制器以特定的方式控制小车的变换。就像骑在真正的带脚轮的小车上一样。这是一个很好的总结/类比吗?
  • 是的。据我所知,在使用 Three 的 VR 渲染时,您无法直接平移相机。然后,我将相机连接到小车上,并希望能够使用游戏手柄输入相对于相机的当前方向旋转和平移小车。

标签: three.js webvr


【解决方案1】:

根据问题上的cmets,我想我明白了。如果我不这样做,请发表评论,我会更新这个答案。

我的理解是,您希望能够相对于相机左右移动,而所有这些都不会改变小车的up 方向。

这实际上比听起来容易,甚至更容易,因为您已经习惯了沿轴平移。

首先,要了解相机有自己的空间参照系,它位于原点,+Y up 方向,向下看 -Z 轴。考虑到这一点,您已经知道“左”和“右”轴:-X (-1, 0, 0) 和 +X (1, 0, 0)。

但是相机(尤其是在 VR 中)在世界空间中可能不会很好地对齐,因此您需要将这些漂亮的统一轴转换为世界轴。 Three.js 使用 Object3D.localToWorld 让这一切变得非常简单。

注意:Object3D.localToWorld 对输入 Vector3 具有破坏性。

这是一个获取与世界对齐的左轴的函数:

const getLeftWorld = (function(){

  const localLeft = new THREE.Vector3(-1, 0, 0);

  return function(vectorRef){ // you can give it a vector to overwrite
    let out = vectorRef || new THREE.Vector3();
    out.copy(localLeft);
    camera.localToWorld(out);
    return out;
  };

))();

您可以为“右”轴创建类似的函数。

有了新的与世界对齐的左轴,您可以使用控制器输入提供的“速度”沿它平移小车。平移不会改变小车的俯仰,尽管它可能会改变高度,具体取决于计算时相机的倾斜方式(但您可以将 y 组件归零就像你以前做的那样,如果你愿意的话)。

【讨论】:

  • 我在使用这种方法时遇到了一些问题。我正在使用更命令式的样式进行演示(cmets 不喜欢在这里编码):gist.github.com/briancw/c95464187b33e385da12edc0d5596a15 结果是:youtu.be/u7WvlzFkPMM
  • @BrianWhicheloe 这很奇怪。即使交换了 Y 轴和 Z 轴,我也从未见过 X 是垂直的系统。我几乎建议做一个程序化版本,其中小车在没有输入的情况下简单地移动一个圆圈,只是为了看看你是否可以让它沿着“地面”以平面方向移动。
【解决方案2】:

这是最适合我的解决方案。我已经从 Brian Peiris 的代码中改编了它:https://github.com/brianpeiris/three-firstperson-vr-controls/blob/master/FirstPersonVRControls.js#L125

// Create a dolly
this.dolly = new THREE.Group()
this.dolly.add(this.camera)
this.scene.add(this.dolly)

// Some variables for movement translations
this.dummy = new THREE.Object3D()
this.dummyDirection = new THREE.Vector3()
this.ZAXIS = new THREE.Vector3(0, 0, 1)
this.moveSpeed = 0.075

// Get controller stick positions
const stickForwardBack = this.leftStick[3]
const stickLeftRight = this.leftStick[2]

// In THREE.js when using the WebXR API, the camera rotation will not update to match the headset orientation.
// You'll need to get pose information from the XRSession or get the xr camera using the following method.
const xrCamera = globals.renderer.xr.getCamera(this.camera)
this.dummy.position.set(0, 0, 0)
this.dummy.quaternion.copy(xrCamera.quaternion)
this.collapseY(this.dummy.quaternion)

// Translate the dummy object forwards/backwards left/right relative to the direction the camera is facing
this.dummy.translateZ(stickForwardBack * this.moveSpeed)
this.dummy.translateX(stickLeftRight * this.moveSpeed)

// Add the dummy position to the dolly
this.dolly.position.add(this.dummy.position)

// Flatten out up and down rotation
collapseY(quaternion) {
    this.dummyDirection.set(0, 0, 1)
    this.dummyDirection.applyQuaternion(quaternion)
    this.dummyDirection.y = 0
    this.dummyDirection.normalize()
    quaternion.setFromUnitVectors(this.ZAXIS, this.dummyDirection)
}

【讨论】: