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

three.js中非单位四元数应用于Vector3与Object3的差异原因咨询

问题描述

我在Three.js中用四元数旋转单位球面上的箭头,两种应用四元数的方法结果不一样:

  • 方法1:把非单位四元数直接应用到Vector3对象上,得到了预期的旋转加缩放效果(蓝色箭头)
  • 方法2:用setRotationFromQuaternion把同一个非单位四元数应用到Object3D对象上,结果和方法1对不上(绿色箭头)

相关代码片段:

非单位四元数定义

// define a quaternion
let u = -0.3; // scaling factor of non-unit quaternion
let t = 0.25*Math.PI; // rotate by angle 2*t

let w = Math.exp(u)*Math.cos(t);
let x = Math.exp(u)*Math.sin(t);
let y = 0;
let z = 0;

let q = { w: w, x: x, y: y, z: z };

这个四元数绕x轴旋转2*t角度,还被exp(u)做了缩放,当u=0时就是单位四元数。

箭头形状定义

// define an arrow shape
const points = [];
points.push( new THREE.Vector3(  0    , 1 ,  0     ) );
...
points.push( new THREE.Vector3(  0    , 1 ,  0     ) );

这些点构成了单位球面上的箭头形状。

两种四元数应用方法

// Method 1: apply quaternion to Vector3 (blue)
let transformedPoints = points.map((point) => {
  return point.clone().applyQuaternion(quaternion);
});
const arrowMaterial1 = new THREE.LineBasicMaterial({color: 0x0000ff});
const geometry1 = new THREE.BufferGeometry().setFromPoints( transformedPoints );
const arrow1 = new THREE.Line( geometry1, arrowMaterial1 );
arrow1.position.set(0, 0, 0);
scene.add(arrow1);

// Method 2: apply quaternion to Object3D (green)
const arrowMaterial2 = new THREE.LineBasicMaterial({color: 0x00ff00});
const geometry2 = new THREE.BufferGeometry().setFromPoints( points );
const arrow2 = new THREE.Line( geometry2, arrowMaterial2 );
arrow2.position.set(0, 0, 0);
arrow2.setRotationFromQuaternion(quaternion);
scene.add(arrow2);

请问这种差异是怎么来的?是因为非单位四元数变换不常用,所以存在没被注意到的bug吗?


问题解答

这不是bug,是Three.js针对两种场景的四元数处理逻辑不一样导致的:

  • Vector3.applyQuaternion()的处理逻辑
    这个方法严格按照四元数变换的数学定义执行:对于向量v和四元数q,变换结果是q * v * q^{-1}。当q是非单位四元数时,计算过程会自然带出缩放效果——因为非单位四元数的逆元是q^{-1} = conjugate(q)/|q|²,最终计算结果会包含|q|的缩放因子(实际是把向量缩放|q|倍)。

  • Object3D.setRotationFromQuaternion()的处理逻辑
    Three.js里Object3D的旋转只负责纯旋转,不处理缩放。setRotationFromQuaternion()内部会自动对传入的四元数做归一化处理,只提取其中的旋转信息,直接忽略缩放因子。不管你传的是不是单位四元数,它都会先转成单位四元数再应用旋转,所以不会产生缩放效果,结果自然和方法1的旋转加缩放不一致。

总结一下:非单位四元数的缩放特性在Vector3变换中会被保留,但在Object3D的旋转设置里会被自动剔除,这是设计上的差异,不是bug。如果想让方法2和方法1效果一致,你可以手动给Object3D加缩放:先算出四元数的模长|q|,然后设置arrow2.scale.setScalar(|q|)就行。

内容的提问来源于stack exchange,提问作者Philip Zhu

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 12:43:13