В настоящее время я работаю с узлами в иерархическом графе сцены, и у меня возникают трудности с правильным переводом/поворотом узла относительно определенного пространства преобразования (например, родительского узла).
Как правильно перевести/повернуть узел относительно его родительского узла в графе сцены?
Проблема
Рассмотрим следующую диаграмму молекулы воды (без соединительных линий) для родительской/дочерней структуры узлов сцены, где атом кислорода O является родительским узлом, а 2 H атомы водорода являются дочерними узлами.
Проблема с переводом
Если вы возьмете родительский атом кислорода O и переместите структуру, вы ожидаете, что дочерние элементы Hводорода последуют за ним и останутся в том же относительном положении относительно своего родителя. Если вместо этого вы возьмете дочерний атом H и переведете его, то это повлияет только на дочерний атом. В общем, как это работает в настоящее время. Когда атомы O перемещаются, атомы H автоматически перемещаются вместе с ними, как и ожидается от иерархического графа.
Однако при переводе родительского элемента дочерние элементы также в конечном итоге накапливают дополнительный перевод, что, по сути, заставляет дочерние элементы "дважды переводить" в одном и том же направлении и отдаляться от своего родитель вместо того, чтобы оставаться на том же относительном расстоянии.
Проблема с вращением
Если вы возьмете родительский узел O и повернете его, вы ожидаете, что дочерние узлы H также будут вращаться, но по орбите, потому что вращение выполняется родительским. Это работает по назначению.
Однако если взять дочерний узел H и указать ему вращаться относительно родителя, я ожидал, что только дочерний узел в конечном итоге будет вращаться вокруг его родитель таким же образом, но этого не происходит. Вместо этого дочерний элемент вращается вокруг своей оси с большей скоростью (например, в два раза быстрее, чем вращается относительно своего локального пространства) в своем текущем положении.
Я действительно надеюсь, что это описание достаточно справедливо, но дайте мне знать, если это не так, и я уточню по мере необходимости.
Математика
Я использую матрицы 4x4 основной столбец (т. е. Matrix4
) и векторы-столбцы (т. е. Vector3
, Vector4
).
Приведенная ниже неверная логика является самой близкой к правильному поведению, которое я нашел. Обратите внимание, что я решил использовать синтаксис, подобный Java, с перегрузкой операторов, чтобы упростить чтение математики. Я пробовал разные вещи, когда думал, что понял это, но на самом деле это не так.
Текущая логика перевода
translate(Vector3 tv /* translation vector */, TransformSpace relativeTo):
switch (relativeTo):
case LOCAL:
localTranslation = localTranslation * TranslationMatrix4(tv);
break;
case PARENT:
if parentNode != null:
localTranslation = parentNode.worldTranslation * localTranslation * TranslationMatrix4(tv);
else:
localTranslation = localTranslation * TranslationMatrix4(tv);
break;
case WORLD:
localTranslation = localTranslation * TranslationMatrix4(tv);
break;
Текущая логика ротации
rotate(Angle angle, Vector3 axis, TransformSpace relativeTo):
switch (relativeTo):
case LOCAL:
localRotation = localRotation * RotationMatrix4(angle, axis);
break;
case PARENT:
if parentNode != null:
localRotation = parentNode.worldRotation * localRotation * RotationMatrix4(angle, axis);
else:
localRotation = localRotation * RotationMatrix4(angle, axis);
break;
case WORLD:
localRotation = localRotation * RotationMatrix4(angle, axis);
break;
Вычисление преобразований мирового пространства
Для полноты мировые преобразования для узла this
вычисляются следующим образом:
if parentNode != null:
worldTranslation = parent.worldTranslation * localTranslation;
worldRotation = parent.worldRotation * localRotation;
worldScale = parent.worldScale * localScale;
else:
worldTranslation = localTranslation;
worldRotation = localRotation;
worldScale = localScale;
Кроме того, полное/накопленное преобразование узла для this
:
Matrix4 fullTransform():
Matrix4 localXform = worldTranslation * worldRotation * worldScale;
if parentNode != null:
return parent.fullTransform * localXform;
return localXform;
Когда преобразование узла запрашивается для отправки в форму шейдера OpenGL, используется матрица fullTransform
.