Извините, если название немного загадочно. Это лучшее, что я мог придумать.
Прежде всего, этот вопрос связан с другим вопросом, который я разместил здесь , но этот вопрос был задан неправильно и в конечном итоге привел к получению ответов, которые могут быть полезны для некоторых людей, которые наткнулись на вопрос, но не решают мою проблему, поскольку она стоит.
Кроме того, отказ от ответственности: я собираюсь опубликовать здесь некоторый код. Я буду аннотировать его, чтобы попытаться сделать его как можно более ясным, что я делаю.
Итак, я использую язык программирования Processing для случайного перемещения коробки (в соответствии с шумом Перлина). Когда коробка движется, я хотел бы, чтобы она вращалась так, чтобы казалось, что она катится. Я не пытаюсь здесь моделировать точную физику, я ищу только визуально приятное решение.
Общие правила вращения заключаются в том, что если ящик движется только вправо, то он должен вращаться вокруг оси Y так, чтобы он вращался вправо, а если ящик движется только вверх, он должен вращаться вокруг оси Y. -ось так, чтобы казалось, что она катится прямо вверх.
Другими словами, если коробка выглядит так:
Тогда, если я скажу: rotate2D(45,0)
я должен получить:
и если я скажу, rotate2D(0,45)
что должен получить:
(для наглядности объявление функции для rotate2D выглядит так rotate2D(horizontal, vertical)
)
Обработка упрощает это с помощью функций вращения, которые она предоставляет, а именно:
rotateX();
rotateY();
rotatez();
Проблема возникает, когда вы хотите вращаться в двух измерениях одновременно. Видите ли, в Обработке, если я позвоню rotateX()
, чтобы повернуть объект вокруг
-ось, на самом деле смещается сама система координат, и все остальные оси идут вместе с ней, поэтому, когда я иду делать rotateY()
после выполнения, rotateX()
я не получу результат, который я хочу, потому что
-ось, вокруг которой я вращаюсь, теперь перекошена. Вы можете увидеть это на картинках выше.
Итак, если я сделаю:
rotateX(radians(45));
rotateY(radians(45));
Я получил:
и если я сделаю:
rotateY(radians(45));
rotateX(radians(45));
Я получил:
Но я хочу что-то вроде:
который я нарисовал, вызвав свою функциюrotate2D()
Проблема в том, что результаты, которые дает моя функция, немного нелогичны. В приведенном выше примере это не очевидно, но, скажем, я хочу сделать: rotate2D(90,90);
. Я думаю, что это создаст коробку, которая выглядит так, как будто она вообще не была повернута, верно? Однако на самом деле он производит:
Это моя текущая реализацияrotate2D()
void rotate2D(float horizontal, float vertical)
{
float[] rotations ={horizontal, vertical};
if(rotations[0]>rotations[1])
{
float tmp = rotations[1];
rotations[1]=rotations[0];
rotations[0]=tmp;
}
if(rotations[1]==vertical) rotateX(radians(rotations[1]-rotations[0]));
else rotateY(radians(rotations[1]-rotations[0]));
float hIncr= (horizontal<0) ? -0.1 : 0.1;
float vIncr= (vertical<0) ? -0.1 : 0.1;
for(float i=0; i<=abs(rotations[0]);i+=0.1)
{
rotateY(radians(hIncr));
rotateX(radians(vIncr));
}
}
Объяснение функции: функция делает следующее (и это все, что вам действительно нужно для решения вопроса с математической точки зрения).
Функция медленно выполняет кучу и оси вращаются сразу друг за другом на небольшую величину каждый раз (0,1 градуса), пока не достигнет верхней границы. Эта верхняя граница является меньшим из значений, которые были переданы функции. Коробка уже будет повернута вокруг правильной оси на разницу между большим и меньшим значением, прежде чем эта последовательность будет иметь место.
Чтобы еще больше уточнить, я ищу математическое объяснение того, почему этот процесс дает противоречивые результаты, которые, надеюсь, прольют свет на решение.
Идеальная конечная цель здесь — найти формулу, которую я могу использовать для имитации вращения вокруг фиксированной оси в нескольких измерениях, когда все, что я могу сделать, — это вращать «мир» по одной оси за раз, как описано выше (что, по сути, эквивалентно вращению относительные оси объектов)
пожалуйста, не стесняйтесь просить меня уточнить дальше. Заранее спасибо.
Прочитав комментарии, я теперь понимаю, что вращения вокруг 2-х осей не коммутируют, и мне не следует ожидать результатов, которых я ожидал ранее.
То, что я вычисляю и передаю в свою функцию, - это чистое вращение в каждом направлении.
Итак, можно ли отредактировать мою формулу, чтобы она производила это чистое вращение? Например, в примере 90/90, если я продолжу последовательность примерно на 37 дополнительных шагов (всего 127), я получу результат, который интуитивно хочу получить) Однако это должно работать для всех комбинаций входных данных.
Извини, Люк, я не думаю, что твое решение работает. У вас должно быть строгое решение, основанное на математике вращений. Проще всего это сделать с помощью кватернионов или, что еще лучше, их аналогов из алгебры Клиффорда, называемых роторами .
Я кратко объясню роторы алгебры Клиффорда и то, как их можно использовать для преобразования последовательных вращений в чистое вращение.
Алгебра Клиффорда
Алгебра Клиффорда вводит произведение векторов, которое некоммутативно, но все же ассоциативно. Если вы знакомы с точечным и перекрестным произведением, оно включает в себя свойства обоих. Учитывая ортогональный базис , имеем следующее:
Опять же, это тоже ассоциативно, так что например. Это создает 8-мерное векторное пространство со следующими базовыми элементами:
Скалярные кратные являются скалярами. Линейные комбинации являются векторами. Линейные комбинации называются бивекторами , и они представляют интерес для вращений. Вращения происходят в плоскостях (только в 3D мы можем сказать, что они вращаются вокруг оси, что эквивалентно), и бивекторы непосредственно описывают плоскости, в которых происходят вращения.
Роторы и вращения
Я заявлю следующее без доказательств, хотя это должно быть знакомо любому, кто хоть немного разбирается в кватернионах.
Задайте ротор как экспоненту бивектора (обычно определяемого через степенной ряд). Позволять быть некоторым единичным бивектором. Затем ротор для некоторого скаляра является
Карта вращения в плоскости, соответствующей под углом принимает форму
где любой вектор.
(Примечание: для тех, у кого есть опыт работы с кватернионами, , , и , так что в этом определении нет расхождений по знаку.)
Применение: нахождение чистого вращения
Использование роторов упрощает математический анализ вращения. Например, давайте рассмотрим вашу проблему: ваши функции вращения вращают глобальную систему координат и сами используют глобальную систему координат в качестве системы отсчета для этих поворотов. Это делает последовательность вращений несколько нетривиальной, но роторы позволяют нам найти более простой способ делать вещи.
Позволять быть исходным кадром. Если вы хотите повернуть вокруг оригинала а потом про оригинал , тогда ваши роторы будут выглядеть так:
Затем мы можем умножить это, чтобы найти чистую плоскость вращения и чистый угол.
Мы можем найти чистый угол поворота к
Давайте рассмотрим случай, как вы предложили, . Тогда у нас должно быть
или .
Чистая плоскость вращения , или вращение вокруг оси .
Изменить: имея это в виду, вот что вам нужно сделать.
rotate(angle,x,y,z)
передаю функцию в единичный вектор и угол? (в чате)Всем в разделе комментариев я хотел бы поблагодарить вас за вашу помощь, поскольку вы привели меня к решению!
Итак, как вы сказали, моя интуиция была неверной, и вращения вокруг нескольких осей не коммутируют.
Чтобы было совершенно ясно, с чем я работаю, я хочу повторить, что значения, передаваемые в мою функцию, представляют желаемое чистое вращение в горизонтальном и вертикальном направлениях соответственно.
поэтому, когда я попробовал rotate2D(90,90), я не получил поворот на 90 градусов в каждом направлении, а скорее компромисс между ними. Другими словами, я пытался использовать длину одной из малых сторон треугольника в качестве его гипотенузы!
Поскольку у меня есть величина каждой малой стороны при вызове функции, я могу просто использовать теорию Пифагора, чтобы определить, какой должна быть длина гипотенузы. Прочитав комментарии, я считаю, что эта гипотенуза эквивалентна единичному вектору результирующего вращения. (пожалуйста, поправьте меня, если я ошибаюсь в этом вопросе.)
Таким образом, используя теорию Пифагора, чтобы определить, как долго продолжать последовательность поворотов на 0,1 градуса на основе желаемых чистых поворотов в горизонтальном и вертикальном направлениях, я могу достичь желаемых результатов!
Вот новый код:
void rotate2D(float horizontal, float vertical)
{
float[] rotations ={horizontal, vertical};
if(rotations[0]>rotations[1])
{
float tmp = rotations[1];
rotations[1]=rotations[0];
rotations[0]=tmp;
}
float c= sqrt(pow(rotations[0],2)+pow(rotations[1],2));
float cIncr=0.1;
float hIncr=(horizontal/(c/cIncr));
float vIncr=(vertical/(c/cIncr));
for(float i=0; i<c; i+=cIncr)
{
rotateY(radians(hIncr));
rotateX(radians(vIncr));
}
}
РЕДАКТИРОВАТЬ: оказалось, что этот метод не работает, в моем методе тестирования был недостаток, но он ДЕЙСТВИТЕЛЬНО производит визуально приятные вращения для моих целей.
Грегго
Эндрю Д. Хван
rotate2D(90,90)
, но повороты вокруг разных (даже перпендикулярных) осей не коммутируют. РасставаниеГрегго
Люк
Грегго
Люк
Грегго
Люк
Эндрю Д. Хван
Люк
Муфрид
Люк