Як обертати предмет навколо осі, орієнтованої на світові?


15

У мене є Vector3, який має кут ейлера для кожної осі.

Зазвичай, коли я хочу створити матрицю обертання, я буду використовувати такі функції, як D3DXMatrixRotationX, передаючи відповідний кут від мого вектора обертання вгорі та помножуючи матриці (ZXY) для створення загальної матриці обертання, яка використовується для формування повної матриці перетворення об'єкта.

Однак цей метод створить набір обертів у просторі об'єкта. Тобто, передача вектора (90, 0, 90) у мій метод дозволить ефективно створити обертання у світовому просторі (90, 90, 0).

Чи є спосіб завжди забезпечити кожен компонент мого вектора обертання в обертанні навколо відповідних осей світового простору?

Редагувати:

Це анімація того, що зараз відбувається - я хочу, щоб спосіб обертався навколо синіх осей, а не червоних.

Кути Ейлера

EDIT 2:

Зауважу лише, що я не шукаю рішення, що включає кути Ейлера, а просто спосіб, яким я можу представити перетворення декількох обертів навколо світових осей.


Що поганого в тому, щоб просто тричі викликати функції Diffenet і фільтрувати частини векторів, які ви не хочете (встановивши їх на 0 перед тим, як викликати функцію)? Інакше я не впевнений, чого ти намагаєшся досягти.
TravisG

Відфільтрувавши що? Я закликаю 3 окремі функції, а потім множу їх, щоб створити матрицю перетворення. Це дозволяє досягти локальної ротації.
Syntac_

Ви хочете кути Ейлера чи обертання навколо світових осей? Зауважте, що за визначенням кутів Ейлера (наприклад, en.wikipedia.org/wiki/Euler_angles ) лише кут альфа суворо стосується світової осі. Інші два кути відносно нахилених осей, які не обов'язково збігаються зі світовими осями.
DMGregory

1
Використовуючи кути Ейлера, ви помножуєте всі три матриці обертання перед нанесенням на вершину. Якщо M, N, O - матриці обертання, результатом операції є MNO v. Я запропонував застосувати кожну матрицю окремо: v1 = O v0, тоді v2 = N v1 і, нарешті, v3 = M v2. Таким чином, кожен vi буде знаходитись у світових координатах, і вам просто потрібно використовувати матрицю обертання для поточної осі у світових координатах.
dsilva.vinicius

3
@ dsilva.vinicius Ваші розділені перетворення точно такі ж, як і комбіновані, або кажучи іншим способом: MNO v == M * (N * (O v))
GuyRT

Відповіді:


1

На основі ваших коментарів, здається, що ви зберігаєте орієнтацію об'єкта як набір кутів Ейлера та / в / зменшуючи кути, коли гравець обертає об'єкт. Тобто у вас є щось на кшталт цього псевдокоду:

// in player input handling:
if (axis == AXIS_X) object.angleX += dir;
else if (axis == AXIS_Y) object.angleY += dir;
else if (axis == AXIS_Z) object.angleZ += dir;

// in physics update and/or draw code:
matrix = eulerAnglesToMatrix(object.angleX, object.angleY, object.angleZ);

Як зазначає Чарльз Бітті , оскільки обертання не змінюється, це не буде працювати, як очікувалося, якщо гравець не поверне об'єкт у тому ж порядку, в якому eulerAnglesToMatrix()застосовується обертання.

Зокрема, розглянемо таку послідовність обертів:

  1. обертати об’єкт на x градусів навколо осі X;
  2. обертати об’єкт на y градусів навколо осі Y;
  3. обертати об’єкт на - x градусів навколо осі X;
  4. обертати об’єкт на - y градусів навколо осі Y.

У наївному представленні кута Ейлера, як це реалізовано в псевдокоді вище, ці обертання скасовуються і об'єкт повернеться до початкової орієнтації. У реальному світі цього не відбувається - якщо ви мені не повірите, візьміть шість-гранулу кубик або кубик Рубіка, нехай x = y = 90 °, і спробуйте самі!

Рішення, як ви зазначаєте у власній відповіді , полягає у збереженні орієнтації об’єкта як матриці обертання (або кватерніона) та оновлення цієї матриці на основі введення користувача. Тобто замість вищевказаного псевдокоду ви зробите щось подібне:

// in player input handling:
if (axis == AXIS_X) object.orientation *= eulerAnglesToMatrix(dir, 0, 0);
else if (axis == AXIS_Y) object.orientation *= eulerAnglesToMatrix(0, dir, 0);
else if (axis == AXIS_Z) object.orientation *= eulerAnglesToMatrix(0, 0, dir);

// in physics update and/or draw code:
matrix = object.orientation;  // already in matrix form!

(Технічно, так як будь-яка матриця повороту або кватерніони може бути представлена у вигляді набору кутів Ейлера, то є їх можна використовувати для зберігання орієнтації об'єкта. Але фізично правильне правило для об'єднання два послідовних поворотів, кожен з яких представлений в вигляді кутів Ейлера, в єдине обертання є досить складним, і по суті означає перетворення обертів в матриці / кватерніони, їх множення, а потім перетворення результату назад в кути Ейлера.)


Так, ви праві, це було рішення. Я вважаю, що це трохи краще, ніж відповідь concept3d, оскільки він створює враження, що кватерніон потрібен, але це неправда. Поки я зберігав поточне обертання як матрицю, а не три кути Ейлера, це було добре.
Syntac_

15

Проблема з обертаннями полягає в тому, що більшість людей думають про це з точки зору кутів Ейлера, оскільки їх легко зрозуміти.

І все ж більшість людей забуває, що кути Ейлера - це три послідовні кути . Це означає, що обертання навколо першої осі зробить наступне обертання відносно першого вихідного обертання, отже, ви не можете самостійно обертати вектор навколо кожної з 3 осей, використовуючи кути Ейлера.

Це безпосередньо перекладається на матриці, коли ви множите дві матриці, ви можете вважати це множення як перетворення однієї матриці на простір іншої матриці.

Це означає, що це трапляється при будь-яких 3 послідовних обертаннях навіть при використанні кватерніонів.

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

Хочу наголосити на тому, що кватерніони не є рішенням для замкнутого замка. Насправді блокування повороту завжди буде, якщо ви представляли кути Ейлера за допомогою кватерніонів. Проблема не в представленні, проблема в 3 послідовних кроках.

Рішення?

Рішенням обертання вектора навколо 3-осі незалежно є поєднання в одну вісь і єдиний кут, таким чином ви зможете позбутися кроку, де вам потрібно зробити послідовне множення. Це ефективно перекладається на:

Моя матриця обертання відображає результат обертання навколо X і Y і Z.

а не Ейлерова інтерпретація

Моя матриця обертання представляє обертання навколо X, а потім Y та Z.

Для уточнення цього я цитую теорему обертання вікіпедії Ейлера:

Відповідно до теореми обертання Ейлера, будь-яке обертання або послідовність обертів жорсткого тіла або системи координат щодо нерухомої точки еквівалентні одному обертанню заданого кута θ щодо нерухомої осі (званої осі Ейлера), який проходить через нерухому точку. Вісь Ейлера зазвичай представлена ​​одиничним вектором u →. Тому будь-яке обертання в трьох вимірах можна представити як комбінацію вектора u → і скалярного θ. Кватерніони дають простий спосіб кодування цього вісь-кута подання в чотирьох числах і застосувати відповідне обертання до вектора позиції, що представляє точку відносно початку в R3.

Зауважте, що множення 3 матриць завжди буде представляти 3 послідовних обертання.

Тепер, щоб поєднати обертання навколо 3 осі, вам потрібно отримати одну вісь і одиничні кути, які представляють обертання навколо X, Y, Z. Іншими словами, для позбавлення від послідовних обертів вам потрібно представити вісь / кут або кватерніон.

Зазвичай це робиться, починаючи з початкової орієнтації (орієнтацію можна розглядати як кут осі), зазвичай представлену як кватерніон або кут осі, а потім модифікуючи цю ординацію для представлення орієнтації вашого призначення. Наприклад, ви починаєте з кватертіона ідентичності, а потім обертаєтесь різницею, щоб досягти орієнтації на призначення. Таким чином ви не втрачаєте жодної міри свободи.


Відповідь позначена як настільки прониклива.
Syntac_

У мене виникають проблеми з з'ясуванням того, що ви намагаєтесь сказати з цією відповіддю. Це просто "не зберігати орієнтацію об'єкта як кути Ейлера"? А якщо так, то чому б просто так не сказати?
Ільмарі Каронен

@IlmariKaronen Це могло б бути чіткіше сказано, але я думаю, що concept3d сприяє представленню осі-кута; Дивіться розділ 1.2.2 цього документа щодо взаємозв'язку між осі-кутом та кватерніонами. Відображення осі кута легше здійснити з вищезазначених причин, воно не страждає від фіксації каркаса, і (принаймні для мене) це так само просто зрозуміти, як і кути Ейлера.
NauticalMile

@ concept3d, це дуже цікаво, і мені дуже подобається ваша відповідь. Для мене не вистачає однієї речі, люди взаємодіють з комп'ютером за допомогою клавіатури та миші, якщо ми думаємо про мишку, то ми говоримо про дельти миші x та y. Як зобразити ці дельти x, y з одним кватерніоном, який ми можемо використовувати для створення матриці обертання, наприклад, для зміни орієнтації об'єкта?
gmagno

@gmagno підхід зазвичай полягає у проектуванні руху миші на об'єктах або сцені та обчисленні дельти в цьому просторі, ви робите це шляхом викидання променя та обчислення перетину. Шукаю рентгенівські кастинги, проект та непроектую, я грубо описуюсь на деталях, так як я не працював у CG роками. сподівання, що допомагає.
concept3d

2

Перемикання комбінації обертів з простору об'єкта на світовий простір є тривіальним: потрібно просто змінити порядок, в якому застосовуються обертання.

У вашому випадку замість множення матриць Z × X × Yпотрібно просто обчислити Y × X × Z.

Обґрунтування цього можна знайти у Вікіпедії: перетворення між внутрішнім та зовнішнім обертанням .


Якби це було правдою, то наступне твердження вашого джерела не було б істинним, тому що обертання були б різними: "Будь-яке зовнішнє обертання еквівалентне внутрішньому обертанню на однакові кути, але із перевернутим порядком елементарних обертів, і навпаки. . "
Syntac_

1
Я не бачу тут суперечності. І моя відповідь, і це твердження вірні. І так, виконуючи обертання в космічному просторі та у світовому просторі дають різні обертання; саме в цьому справа, чи не так?
sam hocevar

Це твердження говорить про те, що зміна порядку завжди призведе до одного і того ж обертання. Якщо одне замовлення виробляє неправильне обертання, інше також буде, тобто це не є рішенням.
Syntac_

1
Ви неправильно читаєте. Зміна порядку не призводить до того ж обертання. Зміна порядку та перехід від внутрішніх обертів до зовнішніх обертів призводить до того ж обертання.
sam hocevar

1
Я не думаю, що я розумію ваше запитання. Ваш GIF показує обертання приблизно на 50 градусів навколо Z(об’єктний простір), потім на 50 градусів навколо X(об’єктний простір), потім на 45 градусів навколо Y(об’єктний простір). Це точно те саме, що обертання на 45 градусів навколо Y( світовий простір ), потім на 50 градусів навколо X( світовий простір ), потім на 50 градусів навколо Z( світовий простір ).
sam hocevar

1

Я надам своє рішення як відповідь, поки хтось не зможе пояснити, чому це працює.

Кожен рендер я будував свій кватерніон за допомогою кутів, збережених у моєму векторі обертання, а потім застосовував кватерніон до мого остаточного перетворення.

Однак для того, щоб утримати його навколо світових осей, мені довелося утримувати кватерніон на всіх кадрах і лише обертати об'єкти, використовуючи різницю кута, тобто.

// To rotate an angle around X - note this is an additional rotation.
// If currently rotated 90, apply this function with angle of 90, total rotation = 180.
D3DXQUATERNION q;
D3DXQuaternionRotation(&q, D3DXVECTOR3(1.0f, 0.0f, 0.0f), fAngle);
m_qRotation *= q; 

//...

// When rendering rebuild world matrix
D3DXMATRIX mTemp;
D3DXMatrixIdentity(&m_mWorld);

// Scale
D3DXMatrixScaling(&mTemp, m_vScale.x, m_vScale.y, m_vScale.z);
m_mWorld *= mTemp;

// Rotate
D3DXMatrixRotationQuaternion(&mTemp, m_qRotation);
m_mWorld *= mTemp;

// Translation
D3DXMatrixTranslation(&mTemp, m_vPosition.x, m_vPosition.y, m_vPosition.z);
m_mWorld *= mTemp;

(Докладний текст для готовності)

Я думаю, dsilva.vinicius намагався дійти до цього моменту.


1

Вам потрібно буде зберігати порядок обертів.

Rotating around x 90 then rotate around z 90 !=
Rotating around z 90 then rotate around x 90.

Зберігайте поточну матрицю обертання та попередньо збільшуйте кожне обертання по мірі їх надходження.


0

На додаток до відповіді @ concept3d ви можете використовувати 3 зовнішні матриці обертання для обертання навколо осі у світових координатах. Цитуючи з Вікіпедії :

Зовнішні обертання - це елементарні обертання, які відбуваються навколо осей нерухомої системи координат xyz. Система XYZ обертається, а ксиз фіксується. Починаючи з перекриття ксизу XYZ, композиція з трьох зовнішніх обертів може бути використана для досягнення будь-якої цільової орієнтації для XYZ. Кути Ейлера або Тайта Браяна (α, β, γ) - амплітуди цих елементарних обертань. Наприклад, цільову орієнтацію можна досягти наступним чином:

Система XYZ обертається навколо осі z на α. Вісь X тепер знаходиться під кутом α відносно осі x.

Система XYZ знову обертається навколо осі x на β. Вісь Z тепер знаходиться під кутом β відносно осі z.

Система XYZ обертається втретє навколо осі z на γ.

Матриці обертання можуть використовуватися для представлення послідовності зовнішніх обертів. Наприклад,

R = Z (γ) Y (β) X (α)

являє собою композицію зовнішніх обертів навколо осей xyz, якщо вона використовується для попереднього примноження векторів колон,

R = X (α) Y (β) Z (γ)

являє собою абсолютно таку саму композицію, коли використовується для множення рядків після множення.

Отже, вам потрібно інвертувати порядок обертів щодо того, що ви робили, використовуючи внутрішні (або локальний простір) обертання. @Syntac попросив обертати zxy, тому нам слід зробити зовнішнє обертання yxz, щоб досягти того ж результату. Код нижче:

Тут пояснення матричних значень .

// Init things.
D3DXMATRIX *rotationMatrixX = new D3DXMATRIX();
D3DXMATRIX *rotationMatrixY = new D3DXMATRIX();
D3DXMATRIX *rotationMatrixZ = new D3DXMATRIX();
D3DXMATRIX *resultRotationMatrix0 = new D3DXMATRIX();
D3DXMATRIX *resultRotationMatrix1 = new D3DXMATRIX();

D3DXMatrixRotationX(rotationMatrixX, angleX);
D3DXMatrixRotationY(rotationMatrixY, angleY);
D3DXMatrixRotationZ(rotationMatrixZ, angleZ);

// yx extrinsic rotation matrix
D3DXMatrixMultiply(resultRotationMatrix0, rotationMatrixY, rotationMatrixX);
// yxz extrinsic rotation matrix
D3DXMatrixMultiply(resultRotationMatrix1, resultRotationMatrix0, rotationMatrixZ);

D3DXVECTOR4* originalVector = // Original value to be transformed;
D3DXVECTOR4* transformedVector = new D3DXVECTOR4();

// Applying matrix to the vector.
D3DXVec4Transform(transformedVector, originalVector, resultRotationMatrix1);

// Don't forget to clean memory!

Цей код є дидактичним, не оптимальним, оскільки ви можете повторно використовувати кілька матриць D3DXMATRIX.


1
вибачте, це невірно. матричне / векторне множення є асоціативним. це точно так само, як комбіноване множення матриць.
concept3d

Ти правий. Я змішував поняття зовнішніх і внутрішніх обертів.
dsilva.vinicius

Я виправлю цю відповідь.
dsilva.vinicius

Відповідь зафіксована зараз.
dsilva.vinicius
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.