Bir nesneyi iki eksende döndürüyorum, neden üçüncü eksen etrafında dönmeye devam ediyor?


38

Bu sorunun altında yatan soruları oldukça sık görüyorum, ancak hepsi belirli bir özelliğin veya aracın özelliklerine kapılmış durumda. İşte size bir çok canlandırılmış örnekle karşımıza çıkan kullanıcılara başvurabileceğimiz kanonik bir cevap oluşturma çabası! :)


Diyelim ki birinci şahıs bir kamera yapıyoruz. Temel fikir, sola ve sağa doğru bakmalı ve yukarı ve aşağı bakmaya eğimli olmalıdır. Böylece şöyle bir kod yazıyoruz (örnek olarak Unity kullanarak):

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

ya da belki

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

Ve çoğunlukla çalışır, ancak zamanla manzara çarpılmaya başlar. Sadece x ve y üzerinde dönmesini söylememize rağmen, fotoğraf makinesi dönme eksenini (z) çeviriyor gibi görünüyor!

Yana doğru eğilen birinci şahıs kameranın animasyonlu örneği

Bu aynı zamanda kamera önünde bir nesneyi manipüle etmeye çalışıyorsak da olabilir - etrafa bakmak istediğimiz bir dünya olduğunu söyleyin:

Yana doğru dönen bir dünyaya animasyonlu örnek

Aynı sorun - bir süre sonra Kuzey kutbu sola veya sağa doğru dolaşmaya başlar. İki eksene girdi veriyoruz ancak bu üçüncüsü için kafa karıştırıcı rotasyonu alıyoruz. Ve tüm rotasyonlarımızı nesnenin yerel eksenleri veya dünyanın küresel eksenleri üzerine uygularsak olur.

Birçok motorda bunu da denetçide göreceksiniz - dünyadaki nesneyi döndürün ve aniden bile dokunmadığımız bir eksen üzerindeki sayılar değişir!

Döndürme açılarının okunması yanında bir nesneyi değiştirmeyi gösteren animasyonlu örnek.  Kullanıcı bu ekseni manipüle etmese bile z açısı değişir.

Peki bu bir motor böceği mi? Programa fazladan rotasyon eklemesini istemediğimizi nasıl söyleriz?

Euler açıları ile ilgisi var mı? Quaternions veya Rotation Matrices mi yoksa Basis Vector mi kullanmalıyım?



1
Aşağıdaki sorunun cevabını da çok yararlı buldum. gamedev.stackexchange.com/questions/123535/…
Travis Pettry

Yanıtlar:


51

Hayır, bu bir motor böceği veya belirli bir rotasyon gösteriminin bir eseri değildir (bunlar da olabilir, ancak bu etki rotasyonları, dahil edilen kuaterniyonları temsil eden her sistem için geçerlidir).

Rotasyonun üç boyutlu uzayda nasıl çalıştığı hakkında gerçek bir gerçeği keşfettiniz ve çeviri gibi diğer dönüşümler hakkındaki sezgilerimizden ayrılıyor:

Rotasyonları farklı bir sırayla uygulamanın farklı sonuçlar verdiğini gösteren animasyonlu örnek

Rotasyonları birden fazla eksende oluşturduğumuzda, elde ettiğimiz sonuç sadece her eksene uyguladığımız toplam / net değer değildir (çeviri için bekleyebileceğimiz gibi). Rotasyonları uyguladığımız sıra, her rotasyon bir sonraki rotasyonların uygulandığı eksenleri (nesnenin yerel eksenleri etrafında dönüyorsa) veya nesneyle eksen arasındaki ilişkiyi (dünya etrafında dönüyorsa) hareket ettirdikçe sonucu değiştirir eksen).

Zaman içinde eksen ilişkilerinin değişmesi, her eksenin ne yapması gerektiği konusunda sezgilerimizi karıştırır. Özellikle, bazı yalpa ve perde dönme kombinasyonları rulo dönmesi ile aynı sonucu verir!

Yerel zift yaw zift dizisini gösteren animasyonlu örnek, tek bir yerel yuvarlanma ile aynı çıktıyı verir.

Her basamağın talep ettiğimiz eksen üzerinde doğru döndüğünü doğrulayabilirsiniz - notasyonumuzda girişimizi engelleyen veya ikinci tahmin eden hiçbir motor arızası veya artefaktı yoktur - dönüşün küresel (veya hipersferik / kuaterniyon) dönüşü, dönüşümlerimiz anlamına gelir etrafında "birbirlerine". Küçük rotasyonlar için lokal olarak ortogonal olabilirler, fakat toplandıkça genel olarak ortogonal olmadıklarını görürüz.

Bu, yukarıdaki gibi 90 derecelik dönüşler için en çarpıcı ve net olanıdır, ancak dönen eksenler, soruda gösterildiği gibi birçok küçük dönüşte de sünmektedir.

Biz bu konuda ne yapacağız?

Zaten bir yalpalama rotasyon sisteminiz varsa, istenmeyen yuvarlanmayı ortadan kaldırmanın en hızlı yollarından biri, nesnenin yerel eksenleri yerine genel veya ana dönüşüm eksenlerinde çalışacak dönüşlerden birini değiştirmektir. Bu şekilde, iki - bir eksen arasındaki çapraz kontaminasyonu alamazsınız, kesinlikle kontrol altında kalır.

İşte yukarıdaki örnekte bir rulo haline gelen aynı pitch-yaw-pitch dizisi, ancak şimdi yaw'ımızı nesnenin yerine global Y ekseni etrafına uyguluyoruz.

Yuvarlanma problemi olmadan, yerel adım ve global yalpa ile dönen bir kupa animasyonlu örneğiGlobal yaw kullanan birinci şahıs kamera animasyonlu örneği

Böylece birinci şahıs kamerayı "Pitch Locally, Yaw Globally" mantığıyla düzeltebiliriz:

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

Rotasyonlarınızı çarpma kullanarak birleştiriyorsanız, aynı efekti elde etmek için çarpmalardan birinin sol / sağ sırasını çevirirsiniz:

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

(Özel sipariş, ortamınızdaki çarpma kurallarına bağlı olacaktır, ancak left = daha genel / sağ = daha yerel, ortak bir seçimdir)

Bu, float değişkenleri olarak istediğiniz net toplam yalpa ve toplam perdeyi depolamakla eşdeğerdir, daha sonra her zaman bir kerede net sonucu uygulamak, sadece bu açılardan tek bir yeni oryantasyon kuaterniyonu veya matrisi oluşturmak (sadece totalPitchkelepçeli kalmanız koşuluyla ):

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

Veya eşdeğer olarak...

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

Bu küresel / yerel bölünmeyi kullanarak, dönüşlerin birbirlerini birleştirme ve etkileme şansları yoktur, çünkü bağımsız eksen kümelerine uygulanırlar.

Aynı fikir, dünyada döndürmek istediğimiz bir nesne ise yardımcı olabilir. Dünya gibi bir örnek için, genellikle onu ters çevirmek ve yawımızı yerel olarak uygulamak (böylece her zaman kutuplarının etrafında döner) ve global olarak adım atmak isteriz (böylece Avustralya’ya yönelmek / uzaklaşmak yerine bizim görüşümüze doğru / uzaklaşır) , işaret ettiği her yer ...)

Daha iyi davranış gösteren rotasyonu gösteren hareketli bir dünya örneği

Sınırlamalar

Bu küresel / yerel melez strateji her zaman doğru çözüm değildir. Örneğin, 3D uçuş / yüzme ile bir oyunda, düz yukarı / düz aşağı işaret ve hala tam kontrol sahibi olmak isteyebilirsiniz. Ancak bu ayar ile gimbal kilidini vuracaksınız - yalpalama ekseniniz (global yukarı) yuvarlanma ekseninize paralel (yerel ileri) ve bükülmeden sola veya sağa bakma şansınız yok.

Bunun gibi durumlarda bunun yerine yapabileceğiniz şey, yukarıdaki soruda başladığımız gibi saf yerel rotasyonları kullanmaktır (bu nedenle kontrolleriniz, nereye bakarsanız bakın aynı hissettirir), bu başlangıçta biraz yuvarlanmasına izin verir - ama sonra bunun için düzeltiriz.

Örneğin, "ileri" vektörümüzü güncellemek için yerel rotasyonları kullanabiliriz, daha sonra son oryantasyonumuzu oluşturmak için bu ileri vektörü bir referans "yukarı" vektörü ile birlikte kullanabiliriz. (Örneğin, Unity's Quaternion.LookRotation yöntemini kullanarak veya bu vektörlerden ortonormal bir matris el ile oluştururken) Yukarı vektörü kontrol ederek, rulo veya bükülmeyi kontrol ederiz.

Uçuş / yüzme örneğinde, bu düzeltmeleri zaman içinde kademeli olarak uygulamak isteyeceksiniz. Çok ani olursa, görüntü dikkat dağıtıcı bir şekilde durur. Bunun yerine, oyuncunun o andaki yukarı vektörünü kullanabilir ve görünümleri düzeye çıkana kadar dikey, kare kare yönlere doğru ipucu kullanabilirsiniz. Bunu bir dönüş sırasında uygulamak bazen oyuncunun kontrolleri boştayken kamerayı bükmekten daha az mide bulandırıcı olabilir.


1
Gif nasıl yaptığını sorabilir miyim? Güzel görünüyorlar.
Bálint

1
@ Bálint LICEcap adlı ücretsiz bir program kullanıyorum - ekranınızın bir bölümünü gif olarak kaydetmenize izin veriyor. Sonra animasyonu kırpıyorum / ek başlık metni ekliyorum / Photoshop kullanarak gif'i sıkıştırıyorum.
DMGregory

Bu sadece Birlik için cevap mı? Web’de daha genel bir cevap var mı?
posfan12

2
@ posfan12 Örnek kod, somutluk için Unity sözdizimini kullanır, ancak ilgili durumlar ve matematik, pano boyunca eşit olarak uygulanır. Örnekleri ortamınıza uygulamakta zorluk çekiyor musunuz?
DMGregory

2
Matematik yukarıda zaten mevcut. Düzenlemenize bakılırsa, yanlış kısımlarını karıştırıyorsunuz. Burada tanımlanan vektör türünü kullanıyorsunuz . Bu, yukarıda açıklandığı gibi bir açılardan bir dönme matrisi oluşturmak için bir yöntem içerir. Bir kimlik matrisi ile başlayın ve çağırın TCVector::calcRotationMatrix(totalPitch, totalYaw, identity)- bu, yukarıdaki "tümü bir kerede Euler" örneğine eşdeğerdir.
DMGregory

1

16 saat sonra ebeveynlik ve rotasyon fonksiyonları lol.

Sadece kodun boş, temelde daireler çizerek ve ayrıca döndükçe ön çevirerek olmasını istemiştim.

Başka bir deyişle amaç, yalpalamayı döndürmek ve yuvarlanma üzerinde herhangi bir etkisi olmadan zifti döndürmektir.

İşte uygulamamda gerçekten işe yarayan birkaç kişi

Birlik İçinde:

  1. Boş bir tane oluşturun. ForYaw de.
  2. Buna boş bir çocuğa ForPitch denir.
  3. Son olarak, bir küp yapın ve z = 5 (ileri) konumuna getirin. Şimdi neyi engellemeye çalıştığımızı görebiliyoruz, ki bu küpü döndürecek (yaw ve zift yaparken kazara "yuvarlanma") .

Şimdi Visual Studio'da komut dosyası kurun, kurulumunuzu yapın ve bununla Update:

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

Ebeveynlik sırasını değiştirmeye çalıştığımda her şeyin benim için kırıldığını unutmayın. Veya eğer Space.World'ü alt perde nesnesi için değiştirirsem (Yaw ebeveyni Space.Self içinden döndürülmeyi umursamaz). Kafa karıştırıcı. Yerel ekseni neden almak zorunda olduğum konusunda kesinlikle bazı açıklamalar kullanabilirim ama bunu dünyadaki uzayda uygulayabilirim - Space.elf neden her şeyi mahvetti?


Bunun, soruyu cevaplamaya yönelik olup olmadığı veya yazdığınız kodun neden bu şekilde çalıştığını anlamak için yardım isteyip istemediğiniz net değil. İkincisi ise, isterseniz bağlam için bu sayfaya bağlayan, yeni bir soru göndermelisiniz. Bir ipucu me.Rotate(me.right, angle, Space.World)olarak me.Rotate(Vector3.right, angle, Space.Self, aynıdır ve iç içe geçmiş dönüştürmeleriniz kabul edilen yanıttaki "Yerel Olarak Yaya Pitch" örnekleriyle eşdeğerdir.
DMGregory

İkisinden de biraz. Kısmen cevabını paylaşmak (örneğin burada tam olarak benim için nasıl çalıştığını). Ve ayrıca soruyu tanıtmak için. Bu ipucu beni düşündürdü. Çok teşekkürler!
Jeh

Şaşırtıcı bu kadar değildi. Eğitimin hala çalışmamasına rağmen kapsamlı cevapları okuduktan sonra, genel Vector3.right ekseni yerine nesnelerin dönme eksenini kullanmanın basit cevabı, dönme nesnesini sabit tutmaya yetti. Aradığım inanılmaz şey buraya 0 oyla ve Google'ın çok sayıda benzer sorusunun 2. sayfasında gömüldü.
user4779
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.