Ориентируйте вращение объекта по касательной к точке сплайна в THREE.JS

Я использую SplineCurve3 для построения линии только по осям X и Y, у меня есть куб, успешно анимированный вдоль этой линии, используя spline.getPoint(t), где t равно 0-1 по времени. Я пытаюсь сориентировать куб к линии с помощью его вектора вверх, который равен Y, с использованием точечного произведения.

Он почти выровнен, но немного выходит наружу. Я подумал, что буду использовать скалярное произведение вектора Y и тангенса текущей точки в качестве угла для поворота кватерниона.

Вот моя функция рендеринга:

function render() {

    var updateMatrix = new THREE.Matrix4(); 
    updateMatrix.setPosition(spline.getPoint(t));

    var angle = new THREE.Vector3(0,1,0).dot(spline.getTangent(t).normalize());

    var quat = new THREE.Quaternion;
    quat.setFromAxisAngle(new THREE.Vector3(0,0,1), angle);
    updateMatrix.setRotationFromQuaternion(quat);

    marker.rotation.getRotationFromMatrix(updateMatrix);
    marker.matrixWorld = updateMatrix;

    t = (t >= 1) ? 0 : t += 0.002;

    renderer.render(scene, camera); 
}

И вот скрипка, демонстрирующая мою проблему, может ли кто-нибудь сказать мне, где я ошибаюсь с аспектом вращения?

Вы можете редактировать мой - пример jsfiddle


person Neil    schedule 24.06.2012    source источник
comment
Я знаю, что это старый пост, но хотел выбросить его уменьшенную версию, которая может оказаться полезной другим: code obj.position.copy(spline.getPoint( t )); obj.lookAt(spline.getTangent( t ).add(obj.position));   -  person manthrax    schedule 08.09.2016


Ответы (1)


Как правило, лучше не связываться с object.matrix напрямую, а вместо этого просто установить объекты position, rotation и scale. Позвольте библиотеке обрабатывать матричные манипуляции. Вам необходимо иметь matrixAutoUpdate = true;.

Чтобы обработать вращающуюся часть, сначала получите касательную к кривой.

tangent = spline.getTangent( t ).normalize();

Вы хотите повернуть объект так, чтобы его вектор вверх (локальный вектор y) указывал в направлении вектора касательной к кривой. Сначала вычислите ось, вокруг которой нужно вращаться. Ось - это вектор, перпендикулярный плоскости, определяемой вектором вверх и вектором касательной. Вы используете кросс-произведение, чтобы получить этот вектор.

axis.crossVectors( up, tangent ).normalize();

Обратите внимание: в вашем случае, поскольку сплайн лежит в плоскости, перпендикулярной оси z, только что вычисленная ось будет параллельна оси z. Однако он может указывать в направлении положительной оси z или отрицательной оси z - это зависит от направления касательного вектора.

Теперь вычислите угол в радианах между вектором вверх и вектором касательной. Скалярное произведение вектора вверх и вектора касательной дает косинус угла между ними (поскольку оба вектора имеют единичную длину). Затем вам нужно взять арккосинус, чтобы получить сам угол.

radians = Math.acos( up.dot( tangent ) );

Теперь извлеките кватернион из оси и угла.

marker.quaternion.setFromAxisAngle( axis, radians );

РЕДАКТИРОВАТЬ: [устаревшая скрипка удалена]

three.js r.69

person WestLangley    schedule 24.06.2012
comment
отредактированный ответ со ссылкой на демонстрацию с использованием r52, теперь использует setEulerFromRotationMatrix (r50) вместо getRotationFromMatrix (r49). - person Neil; 25.10.2012
comment
В последних версиях threejs вместо axis.cross необходимо заменить axis.crossVectors. (Я внес правку ...) Могут быть другие тонкие изменения здесь и там? - person songololo; 14.01.2015
comment
Отличный ответ и спасибо за рабочий пример! - person Pål Thingbø; 30.12.2020
comment
скрипка больше не работает - person user3112634; 20.05.2021