"Глядя на" объект с кватернионом

Итак, я в настоящее время пытаюсь создать функцию, которая возьмет две 3D точки A и B, и предоставит мне кватернион, представляющий поворот, требуемый для точки A, для "точки зрения B" (такой, что точка A локальная Z ось проходит через точку B, если вы это сделаете).

Я изначально нашел этот пост, верхний ответ которого, казалось, дал мне хорошую отправную точку. Я продолжал реализовывать следующий код; вместо того, чтобы предполагать ориентацию по умолчанию (0, 0, -1), как показывает исходный ответ, я пытаюсь извлечь единичный вектор, представляющий фактическую ориентацию камеры.

void Camera::LookAt(sf::Vector3<float> Target)
{
    ///Derived from pseudocode found here:
    ////info/485290/quaternion-rotate-to

    //Get the normalized vector from the camera position to Target
    sf::Vector3<float> VectorTo(Target.x - m_Position.x,
                                Target.y - m_Position.y,
                                Target.z - m_Position.z);
    //Get the length of VectorTo
    float VectorLength = sqrt(VectorTo.x*VectorTo.x +
                              VectorTo.y*VectorTo.y +
                              VectorTo.z*VectorTo.z);
    //Normalize VectorTo
    VectorTo.x /= VectorLength;
    VectorTo.y /= VectorLength;
    VectorTo.z /= VectorLength;

    //Straight-ahead vector
    sf::Vector3<float> LocalVector = m_Orientation.MultVect(sf::Vector3<float>(0, 0, -1));

    //Get the cross product as the axis of rotation
    sf::Vector3<float> Axis(VectorTo.y*LocalVector.z - VectorTo.z*LocalVector.y,
                            VectorTo.z*LocalVector.x - VectorTo.x*LocalVector.z,
                            VectorTo.x*LocalVector.y - VectorTo.y*LocalVector.x);

    //Get the dot product to find the angle
    float Angle = acos(VectorTo.x*LocalVector.x +
                       VectorTo.y*LocalVector.y +
                       VectorTo.z*LocalVector.z);

    //Determine whether or not the angle is positive
    //Get the cross product of the axis and the local vector
    sf::Vector3<float> ThirdVect(Axis.y*LocalVector.z - Axis.z*LocalVector.y,
                                 Axis.z*LocalVector.x - Axis.x*LocalVector.z,
                                 Axis.x*LocalVector.y - Axis.y*LocalVector.x);
    //If the dot product of that and the local vector is negative, so is the angle
    if (ThirdVect.x*VectorTo.x + ThirdVect.y*VectorTo.y + ThirdVect.z*VectorTo.z < 0)
    {
        Angle = -Angle;
    }

    //Finally, create a quaternion
    Quaternion AxisAngle;
    AxisAngle.FromAxisAngle(Angle, Axis.x, Axis.y, Axis.z);

    //And multiply it into the current orientation
    m_Orientation = AxisAngle * m_Orientation;
}

Это почти работает. Случается, что камера, похоже, поворачивает половину расстояния до целевой точки. Если я повторю попытку вращения, он выполняет половину оставшегося вращения до бесконечности, так что если я удерживаю кнопку "Look-at-Button", ориентация камеры становится все ближе и ближе к прямому прицелу, но также постоянно замедляя его вращение, чтобы он никогда не попадал туда.

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

Ответ 1

Нормализовать вектор Axis перед тем, как передать его в FromAxisAngle.

Ответ 2

Почему вы используете кватернион? Вы просто делаете вещи более сложными и требуете больше вычислений в этом случае. Чтобы настроить матрицу: -

calculate vector from observer to observed (which you're doing already)
normalise it (again, doing it already) = at
cross product this with the observer up direction = right
normalise right
cross product at and right to get up

и все готово. Правые, вверх и в векторах - это первая, вторая и третья строки (или столбец, в зависимости от того, как вы настраиваете) своей матрицы. Конечная строка/столбец - это позиция объектов.

Но похоже, что вы хотите преобразовать существующую матрицу в эту новую матрицу на несколько кадров. SLERPs делают это для матриц, а также для кватернионов (что неудивительно, когда вы смотрите на математику). Для преобразования сохраните начальную и целевую матрицы, а затем SLERP между ними, изменив количество на SLERP на каждый кадр (например, 0, 0.25, 0.5, 0.75, 1.0 - хотя нелинейная прогрессия будет выглядеть лучше).

Не забывайте, что вы преобразовываете кватернион обратно в матрицу, чтобы передать его в конвейер рендеринга (если в шейдерах нет каких-либо новых функций для обработки кватернионов). Поэтому любая эффективность, связанная с использованием кватернионов, также должна учитывать процесс преобразования.