Saya memutar objek pada dua sumbu, jadi mengapa benda itu terus berputar di sekitar sumbu ketiga?

38

Saya melihat pertanyaan yang cukup sering muncul yang memiliki masalah mendasar ini, tetapi mereka semua terperangkap dalam rincian fitur atau alat yang diberikan. Berikut adalah upaya untuk membuat jawaban kanonik yang dapat kami rujuk kepada pengguna saat ini muncul - dengan banyak contoh animasi! :)


Katakanlah kita sedang membuat kamera orang pertama. Ide dasarnya adalah harus menguap untuk melihat ke kiri & kanan, dan pitch untuk melihat ke atas & ke bawah. Jadi kami menulis sedikit kode seperti ini (menggunakan Unity sebagai contoh):

void Update() {
    float speed = lookSpeed * Time.deltaTime;

    // Yaw around the y axis using the player's horizontal input.        
    transform.Rotate(0f, Input.GetAxis("Horizontal") * speed, 0f);

    // Pitch around the x axis using the player's vertical input.
    transform.Rotate(-Input.GetAxis("Vertical") * speed,  0f, 0f);
}

atau mungkin

// Construct a quaternion or a matrix representing incremental camera rotation.
Quaternion rotation = Quaternion.Euler(
                        -Input.GetAxis("Vertical") * speed,
                         Input.GetAxis("Horizontal") * speed,
                         0);

// Fold this change into the camera's current rotation.
transform.rotation *= rotation;

Dan sebagian besar berhasil, tetapi seiring waktu tampilan mulai bengkok. Kamera tampaknya berputar pada sumbu gulungannya (z) meskipun kami hanya mengatakannya untuk memutar x dan y!

Contoh animasi dari kamera orang pertama yang miring ke samping

Ini juga dapat terjadi jika kita mencoba memanipulasi objek di depan kamera - katakan itu bola dunia yang ingin kita putar untuk melihat-lihat:

Contoh animasi bola dunia miring ke samping

Masalah yang sama - setelah beberapa saat kutub Utara mulai berjalan ke kiri atau ke kanan. Kami memberi input pada dua sumbu tapi kami mendapatkan rotasi yang membingungkan ini di sepertiga. Dan itu terjadi apakah kita menerapkan semua rotasi kita di sekitar sumbu lokal objek atau sumbu global dunia.

Di banyak mesin Anda juga akan melihat ini di inspektur - putar objek di dunia, dan tiba-tiba angka berubah pada sumbu yang bahkan tidak kita sentuh!

Contoh animasi menunjukkan memanipulasi objek di sebelah pembacaan sudut rotasinya.  Sudut z berubah meskipun pengguna tidak memanipulasi sumbu itu.

Jadi, apakah ini bug mesin? Bagaimana kita memberi tahu program yang tidak kita inginkan untuk menambahkan rotasi tambahan?

Apakah ada hubungannya dengan sudut Euler? Haruskah saya menggunakan Kuarter atau Matriks Rotasi atau Vektor Basis?

DMGregory
sumber
1
Saya menemukan jawaban untuk pertanyaan berikut juga sangat membantu. gamedev.stackexchange.com/questions/123535/…
Travis Pettry

Jawaban:

51

Tidak, ini bukan bug mesin atau artefak dari representasi rotasi tertentu (itu bisa terjadi juga, tetapi efek ini berlaku untuk setiap sistem yang mewakili rotasi, termasuk angka empat).

Anda telah menemukan fakta nyata tentang bagaimana rotasi bekerja di ruang tiga dimensi, dan itu menyimpang dari intuisi kami tentang transformasi lain seperti terjemahan:

Contoh animasi yang menunjukkan bahwa menerapkan rotasi dalam urutan yang berbeda memberikan hasil yang berbeda

Saat kami membuat rotasi pada lebih dari satu sumbu, hasil yang kami dapatkan bukan hanya nilai total / bersih yang kami terapkan pada setiap sumbu (seperti yang mungkin kami harapkan untuk terjemahan). Urutan di mana kita menerapkan rotasi mengubah hasilnya, karena setiap rotasi menggerakkan sumbu di mana rotasi berikutnya diterapkan (jika berputar tentang sumbu lokal objek), atau hubungan antara objek dan sumbu (jika berputar tentang dunia kapak).

Perubahan hubungan sumbu dari waktu ke waktu dapat membingungkan intuisi kita tentang apa yang harus dilakukan oleh setiap sumbu. Secara khusus, kombinasi tertentu dari rotasi yaw dan pitch memberikan hasil yang sama dengan rotasi roll!

Contoh animasi yang menunjukkan urutan pitch-yaw-pitch lokal memberikan output yang sama dengan satu roll lokal

Anda dapat memverifikasi bahwa setiap langkah berputar dengan benar tentang sumbu yang kami minta - tidak ada kesalahan mesin atau artefak dalam notasi kami yang mengganggu atau menebak-nebak input kami - sifat putaran (atau hyperspherical / angka empat) rotasi hanya berarti transformasi kami "bungkus sekitar "satu sama lain. Mereka mungkin ortogonal secara lokal, untuk rotasi kecil, tetapi ketika mereka menumpuk kita mendapati mereka tidak ortogonal secara global.

Ini paling dramatis dan jelas untuk putaran 90 derajat seperti yang di atas, tetapi sumbu pengembara merayap di banyak rotasi kecil juga, seperti yang ditunjukkan dalam pertanyaan.

Jadi, apa yang kita lakukan?

Jika Anda sudah memiliki sistem rotasi pitch-yaw, salah satu cara tercepat untuk menghilangkan roll yang tidak diinginkan adalah mengubah salah satu rotasi untuk beroperasi pada sumbu transformasi global atau induk alih-alih sumbu lokal objek. Dengan begitu Anda tidak bisa mendapatkan kontaminasi silang di antara keduanya - satu sumbu tetap benar-benar terkontrol.

Berikut urutan pitch-yaw-pitch yang sama yang menjadi gulungan pada contoh di atas, tapi sekarang kita menerapkan menguap kita di sekitar sumbu Y global alih-alih objek

Contoh animasi mug yang diputar dengan pitch lokal dan global yaw, tanpa masalah rollContoh animasi kamera orang pertama menggunakan global yaw

Jadi kita dapat memperbaiki kamera orang pertama dengan mantra "Pitch Locally, Yaw Globally":

void Update() {
    float speed = lookSpeed * Time.deltaTime;

    transform.Rotate(0f, Input.GetAxis("Horizontal") * speed, 0f, Space.World);
    transform.Rotate(-Input.GetAxis("Vertical") * speed,  0f, 0f, Space.Self);
}

Jika Anda menambah rotasi Anda menggunakan perkalian, Anda akan membalik urutan kiri / kanan dari salah satu perkalian untuk mendapatkan efek yang sama:

// Yaw happens "over" the current rotation, in global coordinates.
Quaternion yaw = Quaternion.Euler(0f, Input.GetAxis("Horizontal") * speed, 0f);
transform.rotation =  yaw * transform.rotation; // yaw on the left.

// Pitch happens "under" the current rotation, in local coordinates.
Quaternion pitch = Quaternion.Euler(-Input.GetAxis("Vertical") * speed, 0f, 0f);
transform.rotation = transform.rotation * pitch; // pitch on the right.

(Urutan spesifik akan tergantung pada konvensi multiplikasi di lingkungan Anda, tetapi kiri = lebih global / kanan = lebih lokal adalah pilihan umum)

Ini sama dengan menyimpan total yaw total dan pitch total yang Anda inginkan sebagai variabel float, kemudian selalu menerapkan hasil bersih sekaligus, membangun satu angka empat orientasi baru atau matriks dari sudut-sudut ini saja (asalkan Anda tetap totalPitchdijepit):

// Construct a new orientation quaternion or matrix from Euler/Tait-Bryan angles.
var newRotation = Quaternion.Euler(totalPitch, totalYaw, 0f);
// Apply it to our object.
transform.rotation = newRotation;

atau setara ...

// Form a view vector using total pitch & yaw as spherical coordinates.
Vector3 forward = new Vector3(
                    Mathf.cos(totalPitch) * Mathf.sin(totalYaw),
                    Mathf.sin(totalPitch),
                    Mathf.cos(totalPitch) * Mathf.cos(totalYaw));

// Construct an orientation or view matrix pointing in that direction.
var newRotation = Quaternion.LookRotation(forward, new Vector3(0, 1, 0));
// Apply it to our object.
transform.rotation = newRotation;

Menggunakan pemisahan global / lokal ini, rotasi tidak memiliki kesempatan untuk berkembang dan mempengaruhi satu sama lain, karena mereka diterapkan pada set sumbu independen.

Gagasan yang sama dapat membantu jika itu adalah objek di dunia yang ingin kita putar. Untuk contoh seperti bola dunia, kita sering ingin membalikkannya dan menerapkan menguap secara lokal (sehingga selalu berputar di sekitar kutubnya) dan melenggang secara global (sehingga mengarah ke / menjauh dari pandangan kita, daripada ke / jauh dari Australia , dimanapun itu menunjuk ...)

Contoh animasi globe yang menunjukkan rotasi berperilaku lebih baik

Keterbatasan

Strategi hibrid global / lokal ini tidak selalu memperbaiki yang tepat. Misalnya, dalam game dengan penerbangan 3D / berenang, Anda mungkin ingin dapat menunjuk lurus ke atas / lurus ke bawah dan masih memiliki kontrol penuh. Tetapi dengan pengaturan ini Anda akan menekan kunci gimbal - sumbu yaw Anda (global atas) menjadi sejajar dengan sumbu gulungan Anda (penerusan lokal), dan Anda tidak memiliki cara untuk melihat ke kiri atau ke kanan tanpa memutar.

Yang dapat Anda lakukan sebagai gantinya dalam kasus-kasus seperti ini adalah menggunakan rotasi lokal murni seperti yang kami mulai dengan pertanyaan di atas (sehingga kontrol Anda merasakan hal yang sama di mana pun Anda melihat), yang pada awalnya akan membiarkan beberapa gulungan masuk - tetapi kemudian kami memperbaikinya.

Misalnya, kita dapat menggunakan rotasi lokal untuk memperbarui vektor "maju" kami, lalu gunakan vektor maju itu bersama-sama dengan vektor "naik" referensi untuk membangun orientasi akhir kita. (Menggunakan, misalnya, metode Quaternion.LookRotation Unity , atau secara manual membangun matriks ortonormal dari vektor-vektor ini) Dengan mengendalikan vektor ke atas, kita mengontrol gulungan atau pelintiran.

Untuk contoh penerbangan / renang, Anda akan ingin menerapkan koreksi ini secara bertahap dari waktu ke waktu. Jika terlalu tiba-tiba, tampilan bisa bergerak dengan cara yang mengganggu. Sebagai gantinya, Anda dapat menggunakan vektor pemain saat ini dan mengisyaratkannya ke arah vertikal, frame-by-frame, sampai tampilan mereka keluar. Menerapkan ini selama belokan kadang-kadang bisa lebih sedikit mual daripada memutar kamera saat kontrol pemain idle.

DMGregory
sumber
1
Bolehkah saya bertanya bagaimana Anda membuat gif? Mereka terlihat bagus.
Bálint
1
@ Bálint Saya menggunakan program gratis yang disebut LICEcap - ini memungkinkan Anda merekam sebagian layar Anda menjadi gif. Lalu saya memotong animasi / menambahkan teks keterangan tambahan / kompres gif menggunakan Photoshop.
DMGregory
Apakah jawaban ini hanya untuk Persatuan? Apakah ada jawaban yang lebih umum di Web?
posfan12
2
@ posfan12 Contoh kode menggunakan sintaksis Unity untuk konkret, tetapi situasi dan matematika yang terlibat berlaku sama di seluruh papan. Apakah Anda mengalami kesulitan menerapkan contoh ke lingkungan Anda?
DMGregory
2
Matematika sudah ada di atas. Menilai dari hasil edit Anda, Anda hanya menutupi bagian yang salah. Sepertinya Anda menggunakan jenis vektor yang ditentukan di sini . Ini termasuk metode untuk membangun matriks rotasi dari seperangkat sudut seperti yang dijelaskan di atas. Mulailah dengan matriks identitas dan panggil TCVector::calcRotationMatrix(totalPitch, totalYaw, identity)- ini setara dengan contoh "all at once Euler" di atas.
DMGregory
1

SETELAH 16 jam fungsi pengasuhan dan rotasi lol.

Saya hanya ingin memiliki kode yang kosong, pada dasarnya, berputar-putar dan juga membalik depan karena ternyata.

Atau dengan kata lain, tujuannya adalah memutar yaw dan pitch tanpa efek apa pun pada roll.

Berikut adalah beberapa yang benar-benar berfungsi di aplikasi saya

Dalam Unity:

  1. Buat yang kosong. Sebut saja ForYaw.
  2. Beri anak itu kosong, disebut ForPitch.
  3. Akhirnya, buat kubus dan pindahkan ke z = 5 (maju). Sekarang kita dapat melihat apa yang kita coba hindari, yang akan memutar kubus ("roll" yang tidak sengaja saat kita menguap dan melempar) .

Sekarang menulis di Visual Studio, lakukan pengaturan Anda, dan berakhir dengan ini di Pembaruan:

objectForYaw.Rotate(objectForYaw.up, 1f, Space.World);
    objectForPitch.Rotate(objectForPitch.right, 3f, Space.World);

    //this will work on the pitch object as well
    //objectForPitch.RotateAround
    //    (objectForPitch.position, objectForPitch.right, 3f);

Perhatikan bahwa semuanya bangkrut bagi saya ketika saya mencoba memvariasikan urutan pengasuhan. Atau jika saya mengubah Space.World untuk objek pitch anak (orangtua Yaw tidak keberatan diputar melalui Space.Self). Membingungkan Saya pasti bisa menggunakan beberapa klarifikasi tentang mengapa saya harus mengambil poros lokal tetapi menerapkannya melalui ruang dunia - mengapa Space. Sendiri merusak segalanya?

Jeh
sumber
Tidak jelas bagi saya apakah ini dimaksudkan untuk menjawab pertanyaan, atau jika Anda meminta bantuan untuk memahami mengapa kode yang Anda tulis berfungsi seperti ini. Jika itu yang terakhir, Anda harus memposting pertanyaan baru, menghubungkan ke halaman ini untuk konteks jika Anda suka. Sebagai petunjuk, me.Rotate(me.right, angle, Space.World)sama dengan me.Rotate(Vector3.right, angle, Space.Self, dan transformasi bertingkat Anda setara dengan contoh "Pitch Locally, Yaw Globally" dalam jawaban yang diterima.
DMGregory
Sedikit dari keduanya. Sebagian untuk dibagikan dalam jawaban (mis. Inilah cara kerjanya untuk saya). Dan juga untuk memperkenalkan pertanyaan. Petunjuk itu membuat saya berpikir. Terima kasih banyak!
Jeh
Luar biasa ini tidak terbalik. Setelah berjam-jam membaca jawaban yang komprehensif yang meskipun pendidikan masih tidak berfungsi, jawaban sederhana menggunakan sumbu rotasi objek bukan vektor Vector3.right yang tepat adalah semua yang diperlukan untuk menjaga rotasi tetap pada objek. Luar biasa apa yang saya cari terkubur di sini dengan 0 suara dan pada halaman 2 Google banyak, banyak pertanyaan serupa.
user4779