You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

three.js结合Tween+lookAt实现相机动画时意外旋转问题求解

问题原因

你遇到的意外旋转是万向锁奇点导致的:当动画结束时,相机位置为(0,20,0),lookAt目标为(0,0,0),此时相机视线方向为(0,-1,0),和Three.js相机默认的up向量(0,1,0)完全反向共线,lookAt方法无法正常计算相机的水平朝向,就会出现突然跳转的异常。

解决方案
  • 方案1:微调相机终点坐标(改动最小)
    只要让相机终点不要完全落在Y轴上,避免视线和up向量共线即可,视觉上和完全俯瞰没有差异,修改后的代码如下:
function TWEEN_begin_CAMERA_1() {
    const tweenCamera1 = new TWEEN.Tween( 
        {x: camera.position.x, y: camera.position.y, z: camera.position.z, 
            lookAtX: 0, lookAtY: 0, lookAtZ: 0} 
        )
    // 仅修改x、z的终点值为极小的非0值
    .to( {x: 0.001, y: 20, z: 0.001, lookAtX: 0, lookAtY: 0, lookAtZ: 0}, 1000 )

    const updateCamera = function (object, elapsed) {
        camera.position.set(object.x, object.y, object.z);
        camera.lookAt(new THREE.Vector3(object.lookAtX, object.lookAtY, object.lookAtZ))
    }
    tweenCamera1.onUpdate(updateCamera)
    tweenCamera1.start()
}
  • 方案2:修改相机up向量(不需要调整坐标)
    如果你需要相机严格在Y轴正上方,可以手动调整up向量避免和视线共线,如果后续需要恢复默认视角,重新执行camera.up.set(0,1,0)即可:
const updateCamera = function (object, elapsed) {
    camera.position.set(object.x, object.y, object.z);
    camera.up.set(0, 0, 1);
    camera.lookAt(new THREE.Vector3(object.lookAtX, object.lookAtY, object.lookAtZ))
}
  • 方案3:补间相机四元数(最稳定,适合复杂相机动画)
    直接预先计算起始和结束状态的相机朝向四元数进行补间,彻底避免lookAt的计算异常:
function TWEEN_begin_CAMERA_1() {
    // 保存当前相机四元数作为起点
    const startQuat = camera.quaternion.clone();
    // 计算终点朝向四元数
    const endPos = new THREE.Vector3(0,20,0);
    const lookAtTarget = new THREE.Vector3(0,0,0);
    const tempCamera = camera.clone();
    tempCamera.position.copy(endPos);
    tempCamera.up.set(0,0,1);
    tempCamera.lookAt(lookAtTarget);
    const endQuat = tempCamera.quaternion.clone();

    // 补间位置和四元数
    const tweenCamera1 = new TWEEN.Tween({
        x: camera.position.x, y: camera.position.y, z: camera.position.z,
        qx: startQuat.x, qy: startQuat.y, qz: startQuat.z, qw: startQuat.w
    })
    .to({
        x: 0, y: 20, z: 0,
        qx: endQuat.x, qy: endQuat.y, qz: endQuat.z, qw: endQuat.w
    }, 1000)
    .onUpdate(function(obj) {
        camera.position.set(obj.x, obj.y, obj.z);
        camera.quaternion.set(obj.qx, obj.qy, obj.qz, obj.qw);
    })
    .start()
}

内容的提问来源于stack exchange,提问作者Ruslan Karimov

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.25 08:54:07