Bagaimana cara memutar objek di seluruh dunia selaras sumbu?

15

Saya memiliki Vector3 yang memiliki sudut euler untuk setiap sumbu.

Biasanya, ketika saya ingin membuat matriks rotasi saya akan menggunakan fungsi seperti D3DXMatrixRotationX melewati sudut masing-masing dari vektor rotasi saya di atas dan mengalikan matriks (ZXY) untuk membuat matriks rotasi keseluruhan yang digunakan untuk membentuk matriks transformasi objek lengkap.

Namun, metode ini akan menghasilkan seperangkat rotasi di ruang objek. Artinya, melewati vektor (90, 0, 90) ke dalam metode saya akan membuat rotasi di ruang dunia secara efektif (90, 90, 0).

Apakah ada cara untuk selalu memastikan setiap komponen vektor rotasi saya menghasilkan rotasi di sekitar sumbu ruang selaras dunia masing-masing?

EDIT:

Ini adalah animasi dari apa yang sedang terjadi - saya ingin cara memutar sumbu biru, bukan merah.

Euler Angles

EDIT 2:

Hanya untuk dicatat saya tidak mencari solusi yang melibatkan sudut Euler, tetapi hanya cara di mana saya dapat mewakili transformasi beberapa rotasi di seluruh dunia kapak.

Syntac_
sumber
Apa yang salah dengan hanya memanggil fungsi difernet tiga kali dan memfilter bagian vektor yang tidak Anda inginkan (dengan mengaturnya ke 0 sebelum memanggil fungsi)? Kalau tidak, saya tidak yakin apa yang ingin Anda capai.
TravisG
Memfilter apa? Saya memanggil 3 fungsi terpisah dan kemudian mengalikannya untuk membuat matriks transformasi. Ini archieves rotasi lokal sekalipun.
Syntac_
Apakah Anda ingin sudut Euler, atau rotasi tentang sumbu dunia? Perhatikan bahwa dengan definisi sudut Euler (mis. En.wikipedia.org/wiki/Euler_angles ), hanya sudut alfa yang benar-benar mengenai sumbu dunia. Dua sudut lainnya relatif terhadap sumbu miring yang tidak selalu bertepatan dengan sumbu dunia.
DMGregory
1
Menggunakan sudut Euler Anda mengalikan ketiga matriks rotasi sebelum menerapkan kemudian pada vertex. Jika M, N, O adalah matriks rotasi, hasil operasi adalah MNO v. Apa yang saya usulkan adalah untuk menerapkan setiap matriks secara terpisah: v1 = O v0, lalu v2 = N v1 dan akhirnya v3 = M v2. Dengan cara ini setiap vi akan berada di koordinat dunia dan Anda hanya perlu menggunakan matriks rotasi untuk sumbu saat ini di koordinat dunia juga.
dsilva.vinicius
3
@ dsilva.vinicius Transformasi Anda yang terpisah persis sama dengan yang digabungkan, atau dengan kata lain: MNO v == M * (N * (O v))
GuyRT

Jawaban:

1

Berdasarkan komentar Anda, tampaknya Anda menyimpan orientasi objek sebagai satu set sudut Euler , dan dalam / mengurangi sudut ketika pemain memutar objek. Artinya, Anda memiliki sesuatu seperti kodesemu ini:

// 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);

Sebagaimana dicatat oleh Charles Beattie , karena rotasi tidak berpindah-pindah, ini tidak akan berfungsi seperti yang diharapkan kecuali pemain memutar objek dalam urutan yang sama di mana eulerAnglesToMatrix()berlaku rotasi.

Secara khusus, perhatikan urutan rotasi berikut:

  1. putar objek sebanyak x derajat di sekitar sumbu X;
  2. memutar objek dengan derajat y di sekitar sumbu Y;
  3. memutar objek dengan - x derajat di sekitar sumbu X;
  4. memutar objek dengan derajat - y di sekitar sumbu Y.

Dalam representasi sudut Euler naif, seperti yang diterapkan dalam pseudocode di atas, rotasi ini akan dibatalkan dan objek akan kembali ke orientasi aslinya. Di dunia nyata, ini tidak terjadi - jika Anda tidak percaya, ambil dadu enam sisi atau kubus Rubik, biarkan x = y = 90 °, dan coba sendiri!

Solusinya, seperti yang Anda catat dalam jawaban Anda sendiri , adalah untuk menyimpan orientasi objek sebagai matriks rotasi (atau angka empat), dan memperbarui matriks itu berdasarkan input pengguna. Artinya, alih-alih pseudocode di atas, Anda akan melakukan sesuatu seperti ini:

// 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!

(Secara teknis, karena setiap matriks rotasi atau angka empat dapat direpresentasikan sebagai sekumpulan sudut Euler, itu adalah mungkin untuk menggunakan mereka untuk menyimpan orientasi objek. Tapi aturan yang benar secara fisik untuk menggabungkan dua rotasi berurutan, masing-masing direpresentasikan sebagai Euler sudut, menjadi satu rotasi agak rumit, dan pada dasarnya sama dengan mengubah rotasi menjadi matriks / angka empat, mengalikannya, dan kemudian mengubah hasilnya kembali ke sudut Euler.)

Ilmari Karonen
sumber
Ya Anda benar ini solusinya. Saya merasa ini sedikit lebih baik daripada jawaban concept3d karena ia memberi kesan bahwa angka empat diperlukan tetapi itu tidak benar. Selama saya menyimpan rotasi saat ini sebagai matriks dan bukan tiga sudut Euler itu baik-baik saja.
Syntac_
15

Masalah dengan rotasi, adalah bahwa, kebanyakan orang berpikir tentang sudut Euler, karena mudah dipahami.

Namun kebanyakan orang lupa bahwa sudut Euler adalah tiga sudut berurutan . Berarti bahwa rotasi di sekitar sumbu pertama, akan membuat rotasi berikutnya relatif terhadap rotasi asli pertama, maka Anda tidak dapat secara independen memutar vektor di sekitar masing-masing 3 sumbu menggunakan sudut Euler.

Ini secara langsung diterjemahkan menjadi matriks ketika Anda mengalikan dua matriks, Anda dapat menganggap perkalian ini sebagai mentransformasikan satu matriks ke ruang matriks lainnya.

Ini dimaksudkan untuk terjadi dengan 3 rotasi berurutan bahkan ketika menggunakan angka empat.

masukkan deskripsi gambar di sini

Saya ingin menekankan fakta bahwa angka empat bukan solusi untuk kunci gimble. Sebenarnya kunci gimble akan selalu terjadi jika Anda mewakili sudut Euler menggunakan angka empat. Masalahnya bukan representasi, masalahnya adalah yang 3 langkah berurutan.

Solusinya?

Solusi untuk memutar vektor sekitar 3 sumbu secara independen adalah dengan menggabungkan menjadi satu sumbu dan satu sudut, dengan cara ini Anda dapat menyingkirkan langkah di mana Anda harus melakukan perkalian berurutan. Ini akan secara efektif diterjemahkan menjadi:

Matriks rotasi saya menunjukkan hasil rotasi di sekitar X dan Y dan Z.

daripada interpretasi Euler tentang

Matriks rotasi saya mewakili rotasi di sekitar X lalu Y lalu Z.

Untuk memperjelas ini saya akan mengutip dari teorema rotasi wikipedia Euler:

Menurut teorema rotasi Euler, setiap rotasi atau urutan rotasi benda tegar atau sistem Koordinat tentang titik tetap setara dengan rotasi tunggal dengan sudut tertentu θ tentang sumbu tetap (disebut sumbu Euler) yang berjalan melalui titik tetap. Sumbu Euler biasanya diwakili oleh vektor satuan u →. Oleh karena itu, setiap rotasi dalam tiga dimensi dapat direpresentasikan sebagai kombinasi dari vektor u → dan skalar θ. Quaternion memberikan cara sederhana untuk menyandikan representasi sumbu-sudut ini dalam empat angka, dan menerapkan rotasi yang sesuai ke vektor posisi yang mewakili titik relatif ke titik asal pada R3.

Perhatikan bahwa mengalikan 3 matriks akan selalu mewakili 3 rotasi berurutan.

Sekarang inorder untuk menggabungkan rotasi di sekitar 3 sumbu, Anda perlu mendapatkan sumbu tunggal dan sudut tunggal yang mewakili rotasi di sekitar X, Y, Z. Dengan kata lain Anda perlu menggunakan representasi Sumbu / Sudut atau angka empat untuk menyingkirkan rotasi berurutan.

Ini biasanya dilakukan, dengan memulai dengan orientasi awal (orientasi dapat dianggap sebagai sudut sumbu), biasanya direpresentasikan sebagai angka empat atau sudut sumbu, dan kemudian memodifikasi orintasi itu untuk mewakili orientasi tujuan Anda. Misalnya Anda mulai dengan quaterion identitas dan kemudian memutar dengan perbedaan untuk mencapai orientasi tujuan. Dengan cara ini Anda tidak kehilangan derajat kebebasan apa pun.

concept3d
sumber
Ditandai sebagai jawaban yang tampaknya berwawasan luas.
Syntac_
Saya mengalami kesulitan mencari tahu apa yang ingin Anda katakan dengan jawaban ini. Apakah itu hanya "jangan menyimpan orientasi objek sebagai sudut Euler"? Dan jika demikian, mengapa tidak mengatakannya saja?
Ilmari Karonen
@IlmariKaronen Bisa lebih jelas dinyatakan, tapi saya pikir concept3d mempromosikan representasi sumbu-sudut; lihat bagian 1.2.2 dokumen ini untuk hubungan antara sudut-sumbu dan angka empat. Representasi sudut-sumbu lebih mudah diterapkan karena alasan di atas, ia tidak menderita gimbal-lock, dan (setidaknya bagi saya) itu hanya mudah dipahami seperti sudut Euler.
NauticalMile
@ concept3d, itu sangat menarik, dan saya sangat suka jawaban Anda. Ada satu hal yang hilang bagi saya, orang-orang berinteraksi dengan komputer menggunakan keyboard dan mouse, jika kita memikirkan mouse maka kita berbicara tentang delta x dan y mouse. Bagaimana merepresentasikan delta x, y ini dengan satu angka empat yang dapat kita gunakan untuk menghasilkan matriks rotasi, misalnya untuk mengubah orientasi objek?
gmagno
@ Gmagno pendekatan biasanya untuk memproyeksikan pergerakan mouse pada objek atau adegan dan menghitung delta di ruang itu, Anda melakukan ini dengan melemparkan sinar dan menghitung persimpangan. Mencari pengecoran sinar, proyek dan proyek, saya kasar pada detail karena saya tidak bekerja di CG selama bertahun-tahun sekarang. berharap itu bisa membantu.
concept3d
2

Mengubah kombinasi rotasi dari ruang objek ke ruang dunia adalah sepele: Anda hanya perlu membalik urutan penerapan rotasi.

Dalam kasus Anda, alih-alih mengalikan matriks Z × X × Y, Anda hanya perlu menghitung Y × X × Z.

Alasan untuk ini dapat ditemukan di Wikipedia: konversi antara rotasi intrinsik dan ekstrinsik .

sam hocevar
sumber
Jika itu benar maka pernyataan berikut dari sumber Anda tidak akan benar, karena rotasi akan berbeda: "Setiap rotasi ekstrinsik setara dengan rotasi intrinsik oleh sudut yang sama tetapi dengan urutan terbalik dari rotasi unsur, dan sebaliknya . "
Syntac_
1
Saya tidak melihat kontradiksi di sini. Jawaban saya dan pernyataan itu benar. Dan ya, melakukan rotasi di ruang objek dan di ruang dunia menghasilkan rotasi yang berbeda; itulah intinya, bukan?
sam hocevar
Pernyataan itu mengatakan bahwa mengubah urutan akan selalu menghasilkan rotasi yang sama. Jika satu pesanan menghasilkan rotasi yang salah, urutan lainnya juga akan, artinya itu bukan solusi.
Syntac_
1
Anda salah membaca. Mengubah urutan tidak menghasilkan rotasi yang sama. Mengubah urutan dan beralih dari rotasi intrinsik ke rotasi ekstrinsik menghasilkan rotasi yang sama.
sam hocevar
1
Saya rasa saya tidak mengerti pertanyaan Anda. GIF Anda menunjukkan rotasi sekitar 50 derajat Z(ruang objek), lalu 50 derajat di sekitar X(ruang objek), lalu 45 derajat di sekitar Y(ruang objek). Ini persis sama dengan rotasi 45 derajat di sekitar Y( ruang dunia ), kemudian 50 derajat di sekitar X( ruang dunia ), kemudian 50 derajat di sekitar Z( ruang dunia ).
sam hocevar
1

Saya akan memberikan solusi saya sebagai jawaban sampai seseorang dapat menjelaskan mengapa ini berhasil.

Setiap render saya membangun kembali angka empat saya menggunakan sudut yang tersimpan dalam vektor rotasi saya dan kemudian menerapkan angka empat pada transformasi akhir saya.

Namun untuk mempertahankannya di seluruh dunia kapak saya harus mempertahankan angka empat di semua bingkai dan hanya memutar objek menggunakan perbedaan sudut, yaitu ..

// 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;

(Verbose untuk kesesuaian)

Saya pikir dsilva.vinicius sedang mencoba untuk sampai ke titik ini.

Syntac_
sumber
1

Anda harus menyimpan urutan rotasi.

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

Simpan matriks rotasi Anda saat ini dan lakukan premultiply setiap rotasi saat mereka datang.

Charles Beattie
sumber
0

Selain jawaban @ concept3d Anda dapat menggunakan 3 matriks rotasi ekstrinsik untuk memutar sumbu di koordinat dunia. Mengutip dari Wikipedia :

Rotasi ekstrinsik adalah rotasi unsur yang terjadi pada sumbu sistem koordinat tetap xyz. Sistem XYZ berputar, sementara xyz diperbaiki. Dimulai dengan XYZ yang tumpang tindih xyz, komposisi tiga rotasi ekstrinsik dapat digunakan untuk mencapai orientasi target apa pun untuk XYZ. Sudut Euler atau Tait Bryan (α, β, γ) adalah amplitudo dari rotasi unsur ini. Misalnya, orientasi target dapat dicapai sebagai berikut:

Sistem XYZ berputar tentang sumbu z oleh α. Sumbu X sekarang pada sudut α sehubungan dengan sumbu x.

Sistem XYZ berputar lagi tentang sumbu x oleh β. Sumbu Z sekarang pada sudut β sehubungan dengan sumbu z.

Sistem XYZ berputar untuk ketiga kalinya tentang sumbu z oleh γ.

Matriks rotasi dapat digunakan untuk merepresentasikan urutan rotasi ekstrinsik. Contohnya,

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

mewakili komposisi rotasi ekstrinsik tentang sumbu xyz, jika digunakan untuk pra-kalikan vektor kolom, sementara

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

mewakili komposisi yang sama persis ketika digunakan untuk vektor baris pasca-gandakan.

Jadi yang Anda butuhkan adalah membalikkan urutan rotasi sehubungan dengan apa yang akan Anda lakukan menggunakan rotasi intrinsik (atau ruang lokal). @Syntac meminta rotasi zxy, jadi kita harus melakukan rotasi ekstrinsik yxz untuk mencapai hasil yang sama. Kode di bawah ini:

Matriks menghargai penjelasan di sini .

// 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!

Kode ini didaktik, tidak optimal, karena Anda bisa menggunakan kembali beberapa matriks D3DXMATRIX.

dsilva.vinicius
sumber
1
maaf pria ini tidak benar. perkalian matriks / vektor bersifat asosiatif. ini persis sama dengan perkalian matriks gabungan.
concept3d
Kamu benar. Saya telah mencampur konsep rotasi ekstrinsik dan intrinsik.
dsilva.vinicius
Saya akan memperbaiki jawaban ini.
dsilva.vinicius
Jawabannya sudah diperbaiki sekarang.
dsilva.vinicius