视频示例:https://drive.google.com/file/d/18Ep4i1JMs7QvW9m-3U4oyQ4sM0CfIFzP/view
您在这里看到的是,我拥有射线在鼠标下方撞击地球的世界位置。然后我将lookAt()
与THREE.Group
移至该位置,以得到具有正确旋转的四元数。鼠标下方的红点始终表明该四元数很好。接下来,在代表大黄色圆顶中心的四元数中,对鼠标位置的四元数(红点)使用rotateTowards
(内部使用slerp,并且尝试直接使用slerp方法,但结果相同)。并将此四元数设置为跟随鼠标的蓝点的旋转角度。理论上,当我的鼠标离得较远时,这应该始终“粘在”该圆顶上。您可以看到,当我在南半球附近做这些时,确实确实在坚持。但是,在北极附近,它变成了干草堆。它会像应该的那样计算更短的距离,甚至不在正确的大圆上。
相关代码:
// using hammerjs pan events I send an event to the blue sphere with the position on the sphere whats under the mouse, event.point is correct, the red sphere always under the mouse proves this.
this.helperGroup.lookAt(event.point); // To get the requested rotation
const p = this.helperGroup.quaternion.clone(); // helpergroup is just an empty group in (0, 0, 0) to get quaternions with lookAt more easily
// p is now a rotation towards the point under the mouse
const requestedDistance = dome.quaternion.angleTo(p); // dome is the center of the yellow dome in the video, allowedDistance is the arc-length of said dome in radians.
// The reason I rotate the parent of blueSphere because its parent is another group in (0, 0, 0) and the (relative) position of the blue sphere is (0, 0, 1), the planets radius is 1 too.
if (allowedDistance >= requestedDistance) {
blueSphere.parent.lookAt(event.point);
} else {
blueSphere.parent.quaternion.copy(
dome.quaternion.clone().rotateTowards(p, allowedAngle)
);
}
// this snippet is heavily modified for the sake of an example.
更新,以及不同的方法:
我最初使用此
lookAt()
和基于旋转的展示位置来尽可能地避免数学运算。但是它受到了强烈的抨击。因此,现在我只需使用笛卡尔坐标,法线向量和基于简单轴的旋转就可以正确地完成此操作。 (事实证明,使用数学实际上比避免数学更简单)const requestedDistance = blueSphere.angleTo(event.point);
let norm = dome.position.clone().cross(event.point).normalize();
if (allowedDistance >= requestedDistance) {
blueSphere.position.set(event.point); // Not using a group as parent anymore
} else {
blueSphere.position.set(dome.position.clone()
.applyAxisAngle(norm, allowedAngle);
}
最佳答案
两极附近的奇点是四元数slerp函数的一部分。除非使用其他方法,否则无法避免。乔纳森·布鲁(Jonathan Blow)的文章“ Understanding Slerp, Then Not Using It”讨论了slerp函数及其问题,并提出了slerp的替代方法(归一化lerp或nlerp
)是四元数插值器在大多数情况下都是首选的。
请注意,即使该文章中的slerp的C ++代码也承认slerp函数中存在的奇异性。