Quaternion Slerp Three.js дает плохой результат возле полюсов - PullRequest
0 голосов
/ 16 мая 2019

Пример видео: 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);
}

1 Ответ

1 голос
/ 16 мая 2019

Сингулярность около полюсов является частью природы функции кватерниона;этого нельзя избежать, кроме как с помощью другого подхода.В статье Джонатана Блоу " Понимание Slerp, затем не используя его " обсуждается функция Slerp и ее проблемы, и предлагается, чтобы альтернативой slerp (нормализованный lerp или nlerp) был предпочтительный кватернионный интерполяторбольшую часть времени.

Обратите внимание, что даже код C ++ для slerp в этой статье признает особенность, присутствующую в функции slerp.

...