Посмотрите на кватернион, используя вверх вектор - PullRequest
0 голосов
/ 19 сентября 2018

У меня есть камера (в пользовательском 3D-движке), которая принимает кватернион для преобразования вращения.У меня есть две трехмерные точки, представляющие камеру и объект для просмотра.Я хочу вычислить кватернион, который смотрит с камеры на объект, , учитывая мировую ось вверх .

Этот вопрос задает вопросто же самое без вектора «вверх».Все три ответа приводят к тому, что камера направлена ​​в правильном направлении, но вращается (как в случае рыскания / тангажа / крена; представьте, что вы наклоняете голову к уху, глядя на что-то).

Я могу рассчитать ортонормированную основувекторы, которые соответствуют желаемой системе координат:

lookAt = normalize(target - camera)
sideaxis = cross(lookAt, worldUp)
rotatedup = cross(sideaxis, lookAt)

Как я могу создать кватернион из этих трех векторов? Этот вопрос просит то же самое ... но, к сожалению, единственный и принятый ответ говорит ~ "давайте предположим, что вы не заботитесь о крене", а затем игнорирует ось вверх.Я забочусь о ролл.Я не хочу игнорировать верхнюю ось.

Ответы [ 3 ]

0 голосов
/ 27 сентября 2018

Предположим, у вас изначально есть три ортонормированных вектора: worldUp, worldFront и worldSide, и мы можем использовать ваши уравнения для lookAt, sideAxis и rotatedUp.Вектор worldSide не понадобится для достижения результата.

Разбейте операцию на две части.Во-первых, вращайтесь вокруг мира.Затем поверните вокруг стороны оси, которая теперь будет фактически параллельна повернутой стороне мира.

Axis1 = worldUp
Angle1 = (см. Ниже)

Axis2 = cross (lookAt, worldUp) = sideAxis
Angle2 = (см. ниже)

Каждое из этих вращений соответствует кватерниону с использованием:

Q = cos (Angle / 2) + i* Axis_x * sin (Угол / 2) + j * Axis_y * sin (Угол / 2) + k * Axis_z * sin (Угол / 2)

Умножьте оба Q1 и Q2, и вы получите желаемоекватернион.

Сведения об углах:

Пусть P (worldUp) будет проекционной матрицей в направлении worldUp, т.е. P (worldUp) .v = cos (worldUp, v) .worldUpили используя кеты и бюстгальтеры, P (worldUp) = | worldUp>

  1. Проецировать взгляд на плоскость, перпендикулярную worldUp, и нормализовать ее.

    tmp1 = (I - P (worldUp)).lookAt
    n1 = нормализовать (tmp1)

  2. Angle1 = arccos (точка (worldFront, n1))

  3. Angle2= arccos (точка (lookAt, n1))

EDIT1:

Обратите внимание, что нет необходимости вычислять трансцендентные функции.Поскольку скалярное произведение пары нормализованных векторов является косинусом угла и, предполагая, что cos(t) = x, мы имеем тригонометрические тождества:

  • cos(t/2) = sqrt((1 + x)/2)
  • sin(t/2) = sqrt((1 - x)/2)
0 голосов
/ 28 сентября 2018

Предыдущий ответ дал правильное решение с использованием углов.В этом ответе будет представлен альтернативный метод.

Ортогональные базисные векторы, переименовывая их в F = lookAt, R = sideaxis, U = rotatedup, непосредственно образуют столбцы матрицы вращения 3x3, что эквивалентно требуемому кватерниону:

enter image description here

Умножение на вектор эквивалентно использованию указанных компонент вектора в качестве координат в основании камеры.

Матрица вращения 3x3 может бытьпреобразован в кватернион без преобразование в углы / использование дорогостоящих тригонометрических функций.Ниже приведен численно стабильный фрагмент кода C ++, который делает это, возвращая нормализованный кватернион:

inline void CalculateRotation( Quaternion& q ) const {
  float trace = a[0][0] + a[1][1] + a[2][2];
  if( trace > 0 ) {
    float s = 0.5f / sqrtf(trace + 1.0f);
    q.w = 0.25f / s;
    q.x = ( a[2][1] - a[1][2] ) * s;
    q.y = ( a[0][2] - a[2][0] ) * s;
    q.z = ( a[1][0] - a[0][1] ) * s;
  } else {
    if ( a[0][0] > a[1][1] && a[0][0] > a[2][2] ) {
      float s = 2.0f * sqrtf( 1.0f + a[0][0] - a[1][1] - a[2][2]);
      q.w = (a[2][1] - a[1][2] ) / s;
      q.x = 0.25f * s;
      q.y = (a[0][1] + a[1][0] ) / s;
      q.z = (a[0][2] + a[2][0] ) / s;
    } else if (a[1][1] > a[2][2]) {
      float s = 2.0f * sqrtf( 1.0f + a[1][1] - a[0][0] - a[2][2]);
      q.w = (a[0][2] - a[2][0] ) / s;
      q.x = (a[0][1] + a[1][0] ) / s;
      q.y = 0.25f * s;
      q.z = (a[1][2] + a[2][1] ) / s;
    } else {
      float s = 2.0f * sqrtf( 1.0f + a[2][2] - a[0][0] - a[1][1] );
      q.w = (a[1][0] - a[0][1] ) / s;
      q.x = (a[0][2] + a[2][0] ) / s;
      q.y = (a[1][2] + a[2][1] ) / s;
      q.z = 0.25f * s;
    }
  }
}

Источник: http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion

Преобразование этого в соответствии с вашей ситуацией, конечно, просто вопрос обмена.матричные элементы с соответствующими компонентами вектора:

// your code from before
F = normalize(target - camera);   // lookAt
R = normalize(cross(F, worldUp)); // sideaxis
U = cross(R, F);                  // rotatedup

// note that R needed to be re-normalized
// since F and worldUp are not necessary perpendicular
// so must remove the sin(angle) factor of the cross-product
// same not true for U because dot(R, F) = 0

// adapted source
Quaternion q;
double trace = R.x + U.y + F.z;
if (trace > 0.0) {
  double s = 0.5 / sqrt(trace + 1.0);
  q.w = 0.25 / s;
  q.x = (U.z - F.y) * s;
  q.y = (F.x - R.z) * s;
  q.z = (R.y - U.x) * s;
} else {
  if (R.x > U.y && R.x > F.z) {
    double s = 2.0 * sqrt(1.0 + R.x - U.y - F.z);
    q.w = (U.z - F.y) / s;
    q.x = 0.25 * s;
    q.y = (U.x + R.y) / s;
    q.z = (F.x + R.z) / s;
  } else if (U.y > F.z) {
    double s = 2.0 * sqrt(1.0 + U.y - R.x - F.z);
    q.w = (F.x - R.z) / s;
    q.x = (U.x + R.y) / s;
    q.y = 0.25 * s;
    q.z = (F.y + U.z) / s;
  } else {
    double s = 2.0 * sqrt(1.0 + F.z - R.x - U.y);
    q.w = (R.y - U.x) / s;
    q.x = (F.x + R.z) / s;
    q.y = (F.y + U.z) / s;
    q.z = 0.25 * s;
  }
}

(И, разумеется, swap y и z, если вы используете OpenGL.)

0 голосов
/ 24 сентября 2018

lookAt sideaxis rotatedup

Если вы нормализуете эти 3 вектора, это будет составляющая матрицы вращения 3x3.Так что просто конвертируйте эту матрицу вращения в кватернион.

...