XNA - 3D-вращение о локальных (меняющихся) осях корабля - Что мне не хватает? - PullRequest
2 голосов
/ 28 ноября 2011

Я разрабатываю трехмерный космический шутер в XNA как школьный проект (в основном это Астероиды в 3D с усилениями), и я работаю над реализацией крена, тангажа и рыскания относительно локальных осей корабля.(Я должен подчеркнуть: вращение не относится к осям абсолютного / мирового x, y и z.) К сожалению, я боролся с этим в течение последних нескольких недель.Google и мой неолитический мозг обезьяны подвели меня;Может быть, вы, люди, можете помочь!

Вот мои настройки:

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

  • yawRadians, в котором хранятся нужныеотклонение от начальной позиции корабля
  • pitchRadians, в котором хранится желаемый шаг вдали от начальной позиции корабля
  • rollRadians, в котором хранится требуемый крен от начальной позиции корабля

Корабль также поддерживает свои собственные векторы передних, задних, правых, левых, верхних и нижних частей, которые используются как для вращения, так и для движения.(Различные ключи будут продвигать корабль вперед, назад и т. Д. Эта часть отлично работает.)

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

У меня проблема с самими вращениями.Разные решения, которые я пробовал, имели разные результаты.Вот что я сделал до сих пор:

Метод 1 - Yaw, Pitch и Roll относительно абсолютных / мировых осей x, y и z

Сначала я наивно пытался использовать следующее в методе обновления моего корабля:

qYawPitchRoll = Quaternion.CreateFromYawPitchRoll(yawRadians, pitchRadians, rollRadians);     

vFront = Vector3.Transform(vOriginalFront, qYawPitchRoll); 
vBack = -1 * vFront;

vRight = Vector3.Transform(vOriginalRight, qYawPitchRoll);
vLeft = -1 * vRight;

vTop = Vector3.Transform(vOriginalTop, qYawPitchRoll);
vBottom = -1 * vTop;

mShipRotation = Matrix.CreateFromQuaternion(qYawPitchRoll);

(vOriginalFront, vOriginalRight и vOriginalTop просто сохраняют начальную ориентацию корабля.)

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

Я слышал оМетод Quarternion.CreateFromAxisAngle, который звучит идеально.Я мог бы просто объединить три вращения кватернионов, по одному вокруг каждой локальной оси корабля.Что может пойти не так?

Метод 2 - Quaternion.CreateFromAxisAngle

Вот второй фрагмент кода, который я использовал в методе обновления моего корабля:

qPitch = Quaternion.CreateFromAxisAngle(vRight, pitchRadians);
qYaw = Quaternion.CreateFromAxisAngle(vTop, yawRadians);
qRoll = Quaternion.CreateFromAxisAngle(vFront, rollRadians);
qPitchYawAndRoll = Quaternion.Concatenate(Quaternion.Concatenate(qPitch, qYaw), qRoll);

vFront = Vector3.Normalize(Vector3.Transform(vOriginalFront, qPitchYawAndRoll));
vBack = -1 * vFront;
vRight = Vector3.Normalize(Vector3.Transform(vOriginalRight, qPitchYawAndRoll));
vLeft = -1 * vRight;

vTop = Vector3.Normalize(Vector3.Transform(vOriginalTop, qPitchYawAndRoll));
vBottom = -1 * vTop;

mShipRotation = Matrix.CreateFromQuaternion(qPitchYawAndRoll);

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

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

Iтакже пробовал использовать Матрицы напрямую, несмотря на опасения Gimbal Lock:

Метод 3: Матрицы

mShipRotation = Matrix.Identity;

mShipRotation *= Matrix.CreateFromAxisAngle(vRight, pitchRadians);
mShipRotation *= Matrix.CreateFromAxisAngle(vFront, rollRadians);
mShipRotation *= Matrix.CreateFromAxisAngle(vTop, yawRadians);

vFront = Vector3.Normalize(Vector3.Transform(vOriginalFront, mShipRotation));
vBack = -1 * vFront;

vRight = Vector3.Normalize(Vector3.Transform(vOriginalRight, mShipRotation));
vLeft = -1 * vRight;

vTop = Vector3.Normalize(Vector3.Transform(vOriginalTop, mShipRotation));
vBottom = -1 * vTop;

Не повезло;У меня такое же поведение.Одно вращение за раз нормально, но вращение вокруг нескольких осей приводило к тому же причудливому поведению вращения.

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

Обратите внимание, что я регенерирую матрицу mShipRotation при каждом методе обновления, поэтому она не может напрямую накапливать отклонения или неточности.Я думаю, что применение нескольких вращений Quarternion может накапливать ошибку (поскольку я могу сделать один поворот просто отлично), но мои попытки исправить это не сработали.

Короче:

  • Я могу отлично наклонять / вращать / рыскать относительно мировых осей x, y и z.Это просто не то, что ожидал бы игрок, так как крен / качка / рыскание не связаны с кораблем, но с миром.
  • Я могу катиться, наклоняться или рыскать вокруг локальных осей корабля (перед/ Back / Top / Bottom / Left / Right) просто отлично, но только по одному.Любая их комбинация приведет к тому, что корабль будет спирально и быстро деформироваться.

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

Есть идеи?Учитывая ситуацию, когда вам задают крен, угол наклона и рыскание относительно переднего, правого и верхнего векторов корабля, как бы вы пошли на создание матрицы вращения?

Ответы [ 2 ]

2 голосов
/ 28 ноября 2011

Вы, кажется, применяете ваши общие углы (yawRadians, pitchRadians, rollRadians) к вашей локальной оси в ваших методах 2 и 3. Эти значения объединены с мировой осью и не имеют значения в локальном пространстве.Корень вашей проблемы заключается в желании зависеть от 3 углов.

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

Это будет зависеть от ваших общих значений углов (yawRadians, pitchRadians и & rollRadians)и делают их бесполезными, но большинство людей, которые увлекаются трехмерным программированием, в любом случае быстро отказываются от подхода под углом к ​​сохранению ориентации.

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

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

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

Этот блог может помочь: http://stevehazen.wordpress.com/2010/02/15/matrix-basics-how-to-step-away-from-storing-an-orientation-as-3-angles/

1 голос
/ 28 ноября 2011

Я думаю, это может быть то, что вы ищете:

http://forums.create.msdn.com/forums/t/33807.aspx

Я уверен, что CreateFromAxisAngle - это то, что вам нужно.

...