Реализация сложной камеры на основе вращения

Я реализую 3D-движок для пространственной визуализации и пишу камеру со следующими функциями навигации:

  • Поверните камеру (т. Е. Аналогично вращению головы)
  • Поверните вокруг произвольной трехмерной точки (точки в пространстве, которая, вероятно, находится не в центре экрана; камера должна вращаться вокруг нее, сохраняя то же относительное направление взгляда, то есть направление взгляда тоже меняется. Это не смотрит прямо на выбранная точка вращения)
  • Панорамирование в плоскости камеры (поэтому перемещайтесь вверх / вниз или влево / вправо в плоскости, ортогональной вектору взгляда камеры)

Камера не должна вращаться - то есть «вверх» остается вверху. Из-за этого я представляю камеру с местоположением и двумя углами, вращениями вокруг осей X и Y (Z будет вращаться). Затем матрица обзора пересчитывается с использованием местоположения камеры и этих двух углов. Это отлично подходит для панорамирования и вращения глаза, но не для вращения вокруг произвольной точки. Вместо этого я получаю следующее поведение:

  • Сам глаз, по-видимому, движется дальше вверх или вниз, чем должен.
  • Глаз вообще не движется вверх или вниз, когда m_dRotationX равно 0 или пи. (Блокировка кардана? Как этого избежать?)
  • Вращение глаза инвертируется (изменение поворота заставляет его смотреть дальше вверх, когда он должен смотреть дальше вниз, вниз, когда он должен смотреть дальше вверх), когда m_dRotationX находится между пи и 2pi.

(а) Что вызывает этот «дрейф» вращения?

Это может быть карданный замок. Если это так, стандартный ответ на этот вопрос - «использовать кватернионы для представления вращения», о чем много раз говорилось здесь, в SO (1, 2, 3 например), но, к сожалению, без конкретных подробностей (example. Это лучший ответ Я нашел до сих пор; это редко.) Я изо всех сил пытался реализовать камеру, используя кватернионы, объединяющие два вышеупомянутых типа вращения. Фактически, я строю кватернион, используя два поворота, но комментатор ниже сказал, что для этого нет причин - можно сразу построить матрицу.

Это происходит при изменении поворота по осям X и Y (которые представляют направление взгляда камеры) при повороте вокруг точки, но не происходит просто при прямом изменении поворота, то есть при повороте камеры вокруг себя. Для меня это не имеет смысла. Это те же ценности.

(b) Подойдет ли для этой камеры другой подход (например, кватернионы)? Если да, как мне реализовать все три вышеупомянутые функции навигации по камере?

Если другой подход был бы лучше, то, пожалуйста, рассмотрите возможность предоставления конкретного реализованного примера такого подхода. (Я использую DirectX9 и C ++, а также библиотеку D3DX *, которую предоставляет SDK.) Во втором случае я добавлю и назначу награду через пару дней, когда смогу добавить ее к вопросу. Это может звучать так, как будто я прыгаю, но у меня мало времени, и мне нужно быстро реализовать или решить эту проблему (это коммерческий проект с жесткими сроками). Подробный ответ также улучшит архивы SO, потому что большинство ответы камеры, которые я читал, содержат небольшой код.

Спасибо за вашу помощь :)


Некоторые пояснения

Спасибо за комментарии и ответ! Попробую прояснить несколько моментов по проблеме:

  • Матрица обзора пересчитывается из положения камеры и двух углов при изменении одного из этих параметров. Сама матрица никогда не накапливается (т.е. не обновляется) - она ​​пересчитывается заново. Однако положение камеры и две угловые переменные накапливаются (например, всякий раз, когда мышь перемещается, к одному или обоим углам будет добавлено или вычтено небольшое количество в зависимости от количества пикселей, на которые мышь переместилась вверх-вниз и / или влево-вправо на экране.)

  • Комментатор JCooper заявляет, что у меня блокировка подвеса, и мне нужно:

добавьте еще один поворот в ваше преобразование, которое поворачивает eyePos так, чтобы он полностью находился в плоскости y-z, прежде чем применять преобразование, а затем еще один поворот, который после этого перемещает его обратно. Поверните вокруг оси y на следующий угол непосредственно перед и после применения матрицы рыскания-тангажа-крена (один из углов нужно будет отрицать; попробовать это - самый быстрый способ решить, какой). double fixAngle = atan2(oEyeTranslated.z,oEyeTranslated.x);

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

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

В свою очередь, я попытался четко описать проблему, и, если я смогу прояснить ее, дайте мне знать.


Мой текущий код

Чтобы реализовать три вышеупомянутые функции навигации, в событии перемещения мыши, перемещающемся в зависимости от пикселей, перемещенных курсором:

// Adjust this to change rotation speed when dragging (units are radians per pixel mouse moves)
// This is both rotating the eye, and rotating around a point
static const double dRotatePixelScale = 0.001;
// Adjust this to change pan speed (units are meters per pixel mouse moves)
static const double dPanPixelScale = 0.15;

switch (m_eCurrentNavigation) {
    case ENavigation::eRotatePoint: {
        // Rotating around m_oRotateAroundPos
        const double dX = (double)(m_oLastMousePos.x - roMousePos.x) * dRotatePixelScale * D3DX_PI;
        const double dY = (double)(m_oLastMousePos.y - roMousePos.y) * dRotatePixelScale * D3DX_PI;

        // To rotate around the point, translate so the point is at (0,0,0) (this makes the point
        // the origin so the eye rotates around the origin), rotate, translate back
        // However, the camera is represented as an eye plus two (X and Y) rotation angles
        // This needs to keep the same relative rotation.

        // Rotate the eye around the point
        const D3DXVECTOR3 oEyeTranslated = m_oEyePos - m_oRotateAroundPos;
        D3DXMATRIX oRotationMatrix;
        D3DXMatrixRotationYawPitchRoll(&oRotationMatrix, dX, dY, 0.0);
        D3DXVECTOR4 oEyeRotated;
        D3DXVec3Transform(&oEyeRotated, &oEyeTranslated, &oRotationMatrix);
        m_oEyePos = D3DXVECTOR3(oEyeRotated.x, oEyeRotated.y, oEyeRotated.z) + m_oRotateAroundPos;

        // Increment rotation to keep the same relative look angles
        RotateXAxis(dX);
        RotateYAxis(dY);
        break;
    }
    case ENavigation::ePanPlane: {
        const double dX = (double)(m_oLastMousePos.x - roMousePos.x) * dPanPixelScale;
        const double dY = (double)(m_oLastMousePos.y - roMousePos.y) * dPanPixelScale;
        m_oEyePos += GetXAxis() * dX; // GetX/YAxis reads from the view matrix, so increments correctly
        m_oEyePos += GetYAxis() * -dY; // Inverted compared to screen coords
        break;
    }
    case ENavigation::eRotateEye: {
        // Rotate in radians around local (camera not scene space) X and Y axes
        const double dX = (double)(m_oLastMousePos.x - roMousePos.x) * dRotatePixelScale * D3DX_PI;
        const double dY = (double)(m_oLastMousePos.y - roMousePos.y) * dRotatePixelScale * D3DX_PI;
        RotateXAxis(dX);
        RotateYAxis(dY);
        break;
    }

Методы RotateXAxis и RotateYAxis очень просты:

void Camera::RotateXAxis(const double dRadians) {
    m_dRotationX += dRadians;
    m_dRotationX = fmod(m_dRotationX, 2 * D3DX_PI); // Keep in valid circular range
}

void Camera::RotateYAxis(const double dRadians) {
    m_dRotationY += dRadians;

    // Limit it so you don't rotate around when looking up and down
    m_dRotationY = std::min(m_dRotationY, D3DX_PI * 0.49); // Almost fully up
    m_dRotationY = std::max(m_dRotationY, D3DX_PI * -0.49); // Almost fully down
}

И чтобы сгенерировать матрицу просмотра из этого:

void Camera::UpdateView() const {
    const D3DXVECTOR3 oEyePos(GetEyePos());
    const D3DXVECTOR3 oUpVector(0.0f, 1.0f, 0.0f); // Keep up "up", always.

    // Generate a rotation matrix via a quaternion
    D3DXQUATERNION oRotationQuat;
    D3DXQuaternionRotationYawPitchRoll(&oRotationQuat, m_dRotationX, m_dRotationY, 0.0);
    D3DXMATRIX oRotationMatrix;
    D3DXMatrixRotationQuaternion(&oRotationMatrix, &oRotationQuat);

    // Generate view matrix by looking at a point 1 unit ahead of the eye (transformed by the above
    // rotation)
    D3DXVECTOR3 oForward(0.0, 0.0, 1.0);
    D3DXVECTOR4 oForward4;
    D3DXVec3Transform(&oForward4, &oForward, &oRotationMatrix);
    D3DXVECTOR3 oTarget = oEyePos + D3DXVECTOR3(oForward4.x, oForward4.y, oForward4.z); // eye pos + look vector = look target position
    D3DXMatrixLookAtLH(&m_oViewMatrix, &oEyePos, &oTarget, &oUpVector);
}

person David    schedule 13.07.2012    source источник
comment
Я думаю, что эта ошибка связана с ошибкой округления / усечения. если вы увеличиваете диапазон значений, вы уменьшаете дрейф, если я прав   -  person huseyin tugrul buyukisik    schedule 13.07.2012
comment
вы должны использовать более значащие цифры, чем вам нужно   -  person huseyin tugrul buyukisik    schedule 13.07.2012
comment
Последняя цифра является источником округления / усечения   -  person huseyin tugrul buyukisik    schedule 13.07.2012
comment
@ tuğrulbüyükışık: Возможна ошибка с плавающей запятой, но я не уверен, что вы имеете в виду? Углы сохраняются как двойные и представляют собой «целое» вращение (приращение / корректировка матрицы при каждом движении мыши приведет к гораздо большей ошибке; здесь он каждый раз пересчитывается с нуля). Я также не уверен, как это приведет к углу -зависимый дрифт.   -  person David    schedule 13.07.2012


Ответы (4)


Мне кажется, что "Roll" не может быть возможным, учитывая способ, которым вы формируете матрицу просмотра. Независимо от всего остального кода (некоторые из которых выглядят немного забавно), вызов D3DXMatrixLookAtLH(&m_oViewMatrix, &oEyePos, &oTarget, &oUpVector); должен создавать матрицу без прокрутки, если задан [0,1,0] как вектор «вверх», если только oTarget-oEyePos не параллелен вектору вверх. Кажется, это не так, поскольку вы ограничиваете m_dRotationY, чтобы он находился в пределах (-.49pi, +. 49pi).

Возможно, вы сможете уточнить, откуда вы знаете, что происходит «перекат». Есть ли у вас плоскость земли, и линия горизонта этой плоскости земли отклоняется от горизонтали?

Кстати, в UpdateView D3DXQuaternionRotationYawPitchRoll кажется совершенно ненужным, поскольку вы сразу же поворачиваете его и превращаете в матрицу. Просто используйте D3DXMatrixRotationYawPitchRoll, как вы делали в событии мыши. Кватернионы используются в камерах, потому что это удобный способ накапливать повороты, происходящие в координатах глаза. Поскольку вы используете только две оси вращения в строгом порядке, ваш способ накопления углов должен подойти. В векторном преобразовании (0,0,1) тоже нет необходимости. oRotationMatrix уже должен иметь эти значения в записях (_31,_32,_33).


Обновить

Учитывая, что это не вращение, вот проблема: вы создаете матрицу вращения для перемещения глаза в мировых координатах, но хотите, чтобы шаг происходил в камере < / em> координаты. Поскольку крен не разрешен и рыскание выполняется в последнюю очередь, рыскание всегда одинаково как в мире, так и в системе отсчета камеры. Рассмотрим изображения ниже:

Местное вращение

Ваш код отлично работает для локального тангажа и рыскания, потому что они выполняются в координатах камеры.

Нормальный шаг вокруг точки

Но когда вы вращаетесь вокруг контрольной точки, вы создаете матрицу вращения в мировых координатах и ​​используете ее для поворота центра камеры. Это нормально работает, если система координат камеры совпадает с миром. Однако, если вы не проверите, соответствуете ли вы пределу шага, прежде чем повернуть положение камеры, вы получите сумасшедшее поведение, когда достигнете этого ограничения. Камера внезапно начнет кататься по миру - по-прежнему «вращаясь» вокруг контрольной точки, но больше не меняя ориентацию.

Фиксированный шаг вокруг точки

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

Наклон вне оси приведет к крену

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

Ориентация камеры обрабатывается отдельно от перевода

Вместо этого он остается в вертикальном положении, но вы получаете странный перевод.

Один из способов справиться с этим - (1) всегда помещать камеру в каноническое положение и ориентацию относительно контрольной точки, (2) выполнять поворот, а затем (3) возвращать камеру, когда вы закончите (например, аналогично тому, как вы переводите опорную точку в начало координат, применяете вращение Yaw-Pitch, а затем переводите обратно). Однако, если подумать, это, вероятно, не лучший способ.


Обновление 2

Я думаю, что ответ Generic Human, вероятно, лучший. Остается вопрос, какой шаг нужно применить, если вращение вне оси, но пока мы проигнорируем это. Может быть, это даст приемлемые результаты.

Суть ответа такова: перед движением мыши ваша камера находится на c 1 = m_oEyePos и ориентирована на M 1 = D3DXMatrixRotationYawPitchRoll(&M_1,m_dRotationX,m_dRotationY,0). Рассмотрим точку отсчета a = m_oRotateAroundPos. С точки зрения камеры, эта точка равна a '= M 1 (a-c 1).

Вы хотите изменить ориентацию камеры на M 2 = D3DXMatrixRotationYawPitchRoll(&M_2,m_dRotationX+dX,m_dRotationY+dY,0). [Важно: Поскольку вы не позволите m_dRotationY выйти за пределы определенного диапазона, вы должны убедиться, что dY не нарушает это ограничение.] Поскольку камера меняет ориентацию, вам также нужно ее положение для поворота вокруг a к новой точке c 2. Это означает, что a не изменится с точки зрения камеры. Т.е., M 1 (ac 1) == M 2 (ac 2) .

Итак, мы решаем для c 2 (помните, что транспонирование матрицы вращения такое же, как и обратное):

M 2 T M 1 (ac 1) == (ac 2 ) =>

-M 2 T M 1 (ac 1) + a == c 2

Теперь, если мы посмотрим на это как на преобразование, применяемое к c 1, то увидим, что оно сначала отменяется, а затем преобразуется с помощью a , затем повернут на M 1, затем повернут на M 2 T, инвертирован снова, а затем снова переведен a. Это преобразования, с которыми хорошо справляются графические библиотеки, и все они могут быть сведены в единую матрицу преобразования.

@Generic Human заслуживает похвалы за ответ, но вот его код. Конечно, вам нужно реализовать функцию для проверки изменения высоты тона перед его применением, но это просто. В этом коде, вероятно, есть пара опечаток, поскольку я не пытался скомпилировать:

case ENavigation::eRotatePoint: {
    const double dX = (double)(m_oLastMousePos.x - roMousePos.x) * dRotatePixelScale * D3DX_PI;
    double dY = (double)(m_oLastMousePos.y - roMousePos.y) * dRotatePixelScale * D3DX_PI;
    dY = validatePitch(dY); // dY needs to be kept within bounds so that m_dRotationY is within bounds

    D3DXMATRIX oRotationMatrix1; // The camera orientation before mouse-change
    D3DXMatrixRotationYawPitchRoll(&oRotationMatrix1, m_dRotationX, m_dRotationY, 0.0);

    D3DXMATRIX oRotationMatrix2; // The camera orientation after mouse-change
    D3DXMatrixRotationYawPitchRoll(&oRotationMatrix2, m_dRotationX + dX, m_dRotationY + dY, 0.0);

    D3DXMATRIX oRotationMatrix2Inv; // The inverse of the orientation
    D3DXMatrixTranspose(&oRotationMatrix2Inv,&oRotationMatrix2); // Transpose is the same in this case

    D3DXMATRIX oScaleMatrix; // Negative scaling matrix for negating the translation
    D3DXMatrixScaling(&oScaleMatrix,-1,-1,-1);

    D3DXMATRIX oTranslationMatrix; // Translation by the reference point
    D3DXMatrixTranslation(&oTranslationMatrix,
         m_oRotateAroundPos.x,m_oRotateAroundPos.y,m_oRotateAroundPos.z);

    D3DXMATRIX oTransformMatrix; // The full transform for the eyePos.
    // We assume the matrix multiply protects against variable aliasing
    D3DXMatrixMultiply(&oTransformMatrix,&oScaleMatrix,&oTranslationMatrix);
    D3DXMatrixMultiply(&oTransformMatrix,&oTransformMatrix,&oRotationMatrix1);
    D3DXMatrixMultiply(&oTransformMatrix,&oTransformMatrix,&oRotationMatrix2Inv);
    D3DXMatrixMultiply(&oTransformMatrix,&oTransformMatrix,&oScaleMatrix);
    D3DXMatrixMultiply(&oTransformMatrix,&oTransformMatrix,&oTranslationMatrix);

    D3DXVECTOR4 oEyeFinal;
    D3DXVec3Transform(&oEyeFinal, &m_oEyePos, &oTransformMatrix);

    m_oEyePos = D3DXVECTOR3(oEyeFinal.x, oEyeFinal.y, oEyeFinal.z) 

    // Increment rotation to keep the same relative look angles
    RotateXAxis(dX);
    RotateYAxis(dY);
    break;
}
person JCooper    schedule 16.07.2012
comment
Спасибо, JCooper. Это не прокатывает - я добавил некоторые пояснения выше. Вместо этого он кажется «диким», когда m_dRotationX приближается к 0 или пи. - person David; 25.07.2012
comment
Звучит очень полезно - спасибо! Изображение тоже хорошее. Итак, чтобы и перевести глаз, и повернуть его: переместите глаз в плоскость YZ (по X); повернуть на указанное выше fixAngle (возможно, отменено); повернуть вокруг оси X; снова повернуть на фиксированный угол (возможно, отрицательный); перевести обратно; повернуть вокруг оси Y? Хм. Если это не так, не могли бы вы добавить псевдокод, чтобы помочь мне разобраться? - person David; 26.07.2012
comment
Я добавил вознаграждение за пример кода (подробности см. В вопросе). Спасибо за текстовое описание, но у меня возникли проблемы с его переводом на что-то работоспособное. Без сомнения, это моя собственная ошибка / проблема, и ваш ответ правильный! Но я недостаточно хорошо понимаю ваше описание, чтобы закодировать что-то, что работает. - person David; 26.07.2012
comment
@DavidM Я не очень люблю Direct3d, но, наверное, смогу собрать хоть какой-нибудь псевдокод. Однако скажите мне вот что: каково желаемое поведение, если камера наклоняется вокруг контрольной точки, отклоненной на 90 ° вправо? Рассмотрим камеру в зеленой точке на моем снимке, но направленную налево. В этом случае «тангаж» вокруг контрольной точки будет для камеры «креном». Вы не позволяете ему катиться, но вы меняете высоту звука, даже если он вращается вокруг совершенно другой оси. В этом случае кажется странным менять направление вектора взгляда. - person JCooper; 27.07.2012
comment
Пользователь не может щелкнуть точку, расположенную под углом 90 градусов вправо. Я не совсем уверен, что понимаю проблему. Под избеганием крена я имею в виду сохранение горизонта в горизонтальном положении. - person David; 28.07.2012
comment
@DavidM Но пользователь может щелкнуть точку, которая находится не сразу впереди. Поэтому, если вы щелкнете точку, которая находится как можно дальше в сторону, а затем попытаетесь сделать шаг, я не понимаю, что должно произойти. Камера должна перемещаться вверх или вниз вокруг этой точки; но я не думаю, что вы хотите, чтобы он был на такую ​​же величину тильта. Я не знаю, как еще объяснить проблему на данный момент. Я займусь чем-нибудь в понедельник. - person JCooper; 29.07.2012
comment
А, ладно, думаю, я понимаю. Если у вас получится собрать что-нибудь, я буду признателен :) - person David; 30.07.2012
comment
@DavidM Я собрал код. Возможно, это будет отвечать всем требованиям. По сути, я только что закодировал решение Generic Human. - person JCooper; 31.07.2012
comment
Привет, @JCooper - выглядит хорошо ... он не обращается к матрице вида (например, взгляду), правда, только к положению глаз? У меня не было времени попробовать это, но мне нужно назначить награду в течение 30 минут, так что набери себе 150 очков :) - person David; 02.08.2012
comment
@DavidM Хотя это не совсем эффективно, я думаю, что ваш способ вычисления матрицы вида updateView() должен работать нормально, если положение глаз рассчитано правильно. - person JCooper; 02.08.2012
comment
Привет, @JCooper - извини, что так долго не отвечал. Это дает странный эффект при перемещении мыши влево или вправо - штопор камеры поднимается все выше и выше. Любые идеи? - person David; 16.08.2012
comment
@DavidM Я не догадываюсь. Насколько велик эффект штопора по сравнению с рысканием? Всегда ли это происходит? Это происходит, если вы используете горячую клавишу для добавления к dX (чтобы вы знали, что dY не попадает)? Вы реализовали функцию validatePitch? - person JCooper; 18.08.2012

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

Обозначение: A - точка, вокруг которой мы хотим вращаться, C - исходное положение камеры, M - исходная матрица поворота камеры, которая отображает глобальные координаты к локальному окну просмотра камеры.

  1. Запишите локальные координаты A, которые равны A '= M × (A - C).
  2. Поверните камеру, как в обычном режиме «вращения глаз». Обновите матрицу вида M, чтобы она была изменена на M 2, а C оставалась неизменной.
  3. Теперь мы хотим найти C 2 такой, что A '= M 2 × (A - C 2).
    Это легко сделать с помощью уравнения C 2 = A - M 2 < sup> -1 × A '.
  4. Вуаля, камера была повернута, и поскольку локальные координаты A не изменились, A остается в том же месте, с тем же масштабом и расстоянием.

В качестве дополнительного бонуса поведение вращения теперь согласовано между режимами «вращения глаз» и «вращения точек».

person Generic Human    schedule 27.07.2012
comment
Хороший звонок. Это элегантное решение. - person JCooper; 01.08.2012

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

Возможным исправлением может быть сохранение выделенного рыскания / тангажа и относительного положения с точки, когда вы входите в этот режим просмотра, и использование их для выполнения математических расчетов. Для этого требуется немного больше бухгалтерского учета, так как вам необходимо обновлять их при перемещении камеры. Обратите внимание, что это также заставит камеру двигаться, если точка перемещается, что я считаю улучшением.

person Alink    schedule 13.07.2012
comment
Это не повторное применение матриц: матрица обзора затем пересчитывается с использованием местоположения камеры и этих двух углов. Это пересчет, а не объединение существующей матрицы с новой. Накапливаются только два угла (по отдельности) и само положение камеры, которые используются для создания матрицы заново. - person David; 16.07.2012
comment
Если я полностью не понимаю, что вы имеете в виду ... взгляните на методы RotateXAxis() (и Y) и Update() - вы увидите, что Update игнорирует существующее значение m_oViewMatrix и полностью вычисляет новое значение. - person David; 16.07.2012
comment
Да, я имею в виду, что после 10 поворотов m_oEyePos будет результатом 10-кратного применения матрицы вращения к исходному положению, но к m_dRotationX вы просто добавляете dY 10 раз. Большее накопление ошибок для первого заставило меня сказать, что он не движется по идеальному кругу (BTW должно быть легко проверить). Я предположил, что позиционный дрейф был единственным возможным объяснением того, что вы описали (например, вращающаяся точка, выходящая из поля зрения). - person Alink; 21.07.2012

Если я правильно понимаю, вас устраивает компонент вращения в окончательной матрице (за исключением элементов управления инвертированным вращением в задаче № 3), но не часть перевода, так ли это?

Проблема, похоже, связана с тем, что вы относитесь к ним по-разному: вы каждый раз пересчитываете часть вращения с нуля, но накапливаете часть перевода (m_oEyePos). В других комментариях упоминаются проблемы с точностью, но на самом деле это более важно, чем просто точность FP: накопление поворотов из небольших значений рыскания / тангажа просто не то же самое - математически - как выполнение одного большого оборота из накопленного рыскания / тангажа. Отсюда несоответствие вращения / перемещения. Чтобы исправить это, попробуйте пересчитать положение глаз с нуля одновременно с вращением, аналогично тому, как вы находите «oTarget = oEyePos + ...»:

oEyePos = m_oRotateAroundPos - dist * D3DXVECTOR3(oForward4.x, oForward4.y, oForward4.z)

dist можно зафиксировать или рассчитать по старому положению глаз. Это сохранит точку вращения в центре экрана; в более общем случае (который вас интересует) -dist * oForward здесь следует заменить старым / начальным m_oEyePos - m_oRotateAroundPos, умноженным на старый / начальный поворот камеры, чтобы перенести его в пространство камеры (поиск постоянного вектора смещения в системе координат камеры) , затем умножается на инвертированный поворот новой камеры, чтобы получить новое направление в мире.

Это, конечно, будет зависеть от блокировки кардана, когда шаг будет прямым вверх или вниз. Вам нужно будет точно определить, какое поведение вы ожидаете в этих случаях, чтобы решить эту часть. С другой стороны, блокировка при m_dRotationX = 0 или = pi довольно странна (это рыскание, а не шаг, верно?) И может быть связана с вышеизложенным.

person vpozdyayev    schedule 31.07.2012