Слепящий поворот зеркал

9

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

        transform.rotation = Quaternion.Slerp(startQuaternion, lookQuaternion, turningNormalizer*turningSpeed/10f)

startQuaternion - это текущее вращение персонажа, когда дается новая цель.

lookQuaternion - это направление, в котором должен смотреть персонаж, и оно установлено так:

    destinationVector = currentWaypoint.transform.position - transform.position;
    lookQuaternion = Quaternion.LookRotation(destinationVector, Vector3.up);

turnNormalizer просто Time.deltaTimeувеличивается и turningSpeedявляется статическим значением, заданным в редакторе.

Проблема в том, что, хотя персонаж поворачивается, как и должно быть, большую часть времени, у него есть проблемы, когда он должен приблизиться к 180 градусам. Тогда это иногда дрожит и отражает вращение:

введите описание изображения здесь

На этом плохо нарисованном изображении персонаж (справа) начинает поворачиваться к кругу слева. Вместо того, чтобы просто повернуть налево или направо, начинается «зеркальный танец»:

  1. Он начинает вращаться в направлении новой облицовки
  2. Затем он внезапно поворачивается на тот же угол, но на другой стороне и продолжает вращаться

Он делает это «зеркальное отображение» так долго, пока не смотрит на цель. Это что-то с кватернионами, сползанием / лепрпингом или чем-то еще?

РЕДАКТИРОВАТЬ 1: Очевидно, проблема не возникает из-за самого вращения. Более вероятная проблема заключается в том, что персонаж движется по направлению к лицу во время его вращения. Ограничение угла между лицом и целью, когда персонажу позволено двигаться, иногда уменьшает вращение дрожания / отражения.

Это, конечно, вызывает больше вопросов, почему персонаж не может двигаться и вращаться одновременно без проблем? Код, используемый для движения:

transform.Translate(Vector3.forward * runningSpeed/10f * Time.deltaTime);
Esa
источник
'LookQuaternion' обновляется каждый кадр? Если это так, то я предполагаю, что некоторые небольшие неточности приводят к некоему джиттеру, когда игрок «отклоняется» от поворота взгляда, и поэтому новое направление взгляда находится на другой стороне «прямо позади» ... .
Стивен Stadnicki

Ответы:

5

Каждая ориентация в трехмерном пространстве может быть представлена ​​двумя отдельными единичными кватернионами qи -q(по компонентам отрицается q). Например, ориентация, представленная единичной матрицей 3х3, Iможет быть представлена ​​двумя кватернионами:

 q:  { 0,  0,  0},  1
-q:  {-0, -0, -0}, -1

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

Результат, который вы наблюдаете, когда slerp берет более длинную дугу для вращения, это когда slerping между 2 кватернионами, которые не лежат в одном и том же полушарии. Если это произойдет, просто отвергните одного из них перед тем, как его сперлинговать, тогда сперльса займет более короткую дугу.

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

Майк Земдер
источник
Интересно, правильно ли я вас понял, когда пытался это сделать, if(Quaternion.Dot (lookQuaternion, startQuaternion) < 0) { startQuaternion = Quaternion.Inverse(startQuaternion); }но это не решило проблему. Извините за плохое форматирование, я не могу приручить этот раздел комментариев.
Esa
Почти, но это не Inverseсовсем другая операция Negate. if(Quaternion.Dot (lookQuaternion, q) < 0) { q.x = -q.x; q.y = -q.y; q.z = -q.z; q.w = -q.w; }Я не использую C #, но, судя по документу, вы также можете использовать функцию Negate напрямую.
Майк Земдер
if(Quaternion.Dot (lookQuaternion, startQuaternion) < 0) { startQuaternion = Quaternion.Negate(startQuaternion); }
Майк Земдер
Боюсь, что это не решило проблему. Я обновил вопрос.
Esa
Да, это, наверное, смесь разных ошибок. Упростите настройку теста, тестируйте одну вещь за раз, а не перевод и ротацию одновременно. Сначала убедитесь, что одно работает, затем другое, а затем оба вместе. Сначала сосредоточьтесь только на вращении, используйте некоторые хорошо известные значения ориентации, например, начните с 170 градусов, двигайтесь вниз до нуля, посмотрите, где он идет не так, и
опубликуйте
1

Ну, ваша проблема в том, что эти два вектора, которые образуют ваши кватернионы, составляют 180 градусов, поэтому вопрос, который следует задать при интерполяции, какую дугу он должен принять? верхняя дуга или нижняя ??

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

По этой ссылке .

Когда тета равна 180 градусам, результат не определен, потому что нет кратчайшего направления для поворота, я не думаю, что эта ситуация еще обрабатывается правильно, было бы лучше, если бы мы выбрали какую-то произвольную ось, которая является нормальной для qa или qb, я бы цените любые идеи для лучшего способа сделать это.

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

concept3d
источник
1

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

Теперь, для большинства точек на сфере, близлежащие точки соответствуют близким большим кругам; например, меридианы, на которых лежат Лос-Анджелес и Феникс, довольно близки друг к другу. Но когда пункт назначения является антиподом исходной точки - с южного полюса к северному полюсу начальной точки - больше нет ни одного большого круга через них обоих, а вместо этого все большие круги одного проходят через другой. Что еще хуже, это означает, что точки около южного полюса не являются «большинством точек»; две точки рядом друг с другом и у южного полюса могут иметь сильно расходящиеся меридианы.

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

Что касается того, что с этим делать, самое лучшее, что приходит на ум, это не пересчитывать «целевое» направление взгляда в каждом кадре; вместо этого используйте один и тот же для диапазона нескольких кадров, затем вычислите новый и используйте его для нескольких кадров и т. д. При необходимости вы можете даже интерполировать от одной цели к следующей по промежутку в несколько кадров, чтобы направление движения не меняется слишком резко.

Стивен Стадницки
источник