Widzę pytania, które pojawiają się dość często, które dotyczą tego podstawowego problemu, ale wszystkie są uwikłane w szczegóły danej funkcji lub narzędzia. Oto próba stworzenia kanonicznej odpowiedzi, do której możemy skierować użytkowników, kiedy to się pojawi - z dużą ilością animowanych przykładów! :)
Powiedzmy, że tworzymy aparat z perspektywy pierwszej osoby. Podstawową ideą jest to, że należy ziewać, aby spojrzeć w lewo i w prawo, a wysokość, aby spojrzeć w górę i w dół. Piszemy więc trochę takiego kodu (na przykładzie Unity):
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);
}
albo może
// 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;
I to w większości działa, ale z czasem widok zaczyna się krzywić. Wygląda na to, że kamera obraca się wokół swojej osi obrotu (z), chociaż powiedzieliśmy, aby obracała się tylko na x i y!
Może się to również zdarzyć, jeśli próbujemy manipulować obiektem przed kamerą - powiedzmy, że jest to kula ziemska, którą chcemy obrócić, aby się rozejrzeć:
Ten sam problem - po chwili biegun północny zaczyna błądzić w lewo lub w prawo. Dajemy wkład w dwie osie, ale uzyskujemy ten mylący obrót w trzeciej. I dzieje się tak, czy zastosujemy wszystkie nasze obroty wokół lokalnych osi obiektu, czy globalnych osi świata.
W wielu silnikach zobaczysz to również w inspektorze - obracaj obiekt na świecie, a nagle liczby zmieniają się na osi, której nawet nie dotknęliśmy!
Czy to błąd silnika? Jak powiedzieć programowi, że nie chcemy, aby zwiększał rotację?
Czy ma to coś wspólnego z kątami Eulera? Czy zamiast tego powinienem stosować ćwiartki, macierze obrotu lub wektory podstawowe?
źródło
Odpowiedzi:
Nie, to nie jest błąd silnika lub artefakt określonej reprezentacji obrotu (mogą się one również zdarzyć, ale ten efekt dotyczy każdego systemu, który reprezentuje obroty, włącznie z czwartorzędami).
Odkryłeś prawdziwy fakt na temat działania rotacji w przestrzeni trójwymiarowej i odbiega ona od naszej intuicji dotyczącej innych transformacji, takich jak tłumaczenie:
Kiedy tworzymy rotacje na więcej niż jednej osi, otrzymujemy wynik nie tylko całkowitą / netto wartość, którą zastosowaliśmy do każdej osi (jak można się spodziewać w tłumaczeniu). Kolejność, w jakiej stosujemy obroty, zmienia wynik, ponieważ każdy obrót przesuwa osie, na których stosowane są następne obroty (jeśli obraca się wokół lokalnych osi obiektu) lub relację między obiektem a osią (jeśli obraca się wokół świata osie).
Zmiana relacji osi w czasie może mylić naszą intuicję co do tego, co każda z osi ma „robić”. W szczególności niektóre kombinacje obrotu w odchyleniu i skoku dają taki sam wynik, jak w przypadku obrotu!
Możesz sprawdzić, czy każdy krok obraca się prawidłowo wokół żądanej osi - nie ma usterki silnika ani artefaktu w naszym zapisie, który koliduje z naszym wejściem lub zgaduje go z drugiej strony - sferyczny (lub nadprzestrzenny / czwartorzędowy) obrót oznacza po prostu nasze zawijanie transformacji wokół siebie. Mogą być lokalnie ortogonalne, dla małych rotacji, ale gdy się gromadzą, okazuje się, że nie są globalnie ortogonalne.
Jest to najbardziej dramatyczne i wyraźne dla zakrętów o 90 stopni, jak te powyżej, ale osie wędrujące pełzają również przy wielu małych obrotach, jak pokazano w pytaniu.
Co więc z tym zrobimy?
Jeśli masz już system obrotu z odchyleniem skoku, jednym z najszybszych sposobów wyeliminowania niepożądanego przechylenia jest zmiana jednego z obrotów, aby działał na globalnej lub macierzystej osi transformacji zamiast na lokalnych osiach obiektu. W ten sposób nie można uzyskać zanieczyszczenia krzyżowego między nimi - jedna oś pozostaje całkowicie kontrolowana.
Oto ta sama sekwencja pitch-yaw-pitch, która stała się rzutem w powyższym przykładzie, ale teraz stosujemy nasze odchylenie wokół globalnej osi Y zamiast obiektu
Możemy więc naprawić kamerę pierwszoosobową za pomocą mantry „Pitch Locally, Yaw Global”:
Jeśli sumujesz rotacje za pomocą mnożenia, odwróć lewą / prawą kolejność jednego z mnożeń, aby uzyskać ten sam efekt:
(Konkretna kolejność będzie zależeć od konwencji mnożenia w twoim środowisku, ale lewy = więcej globalny / prawy = więcej lokalny jest częstym wyborem)
Jest to równoważne z zapamiętywaniem całkowitego odchylenia netto i skoku całkowitego, który chcesz jako zmienne zmiennoprzecinkowe, a następnie zawsze stosowanie wyniku netto naraz, konstruowanie pojedynczej czwartorzędowej orientacji czwartorzędowej lub macierzy z tych samych kątów (pod warunkiem, że trzymasz się
totalPitch
zaciśniętego):lub równoważnie ...
Korzystając z tego podziału globalnego / lokalnego, obroty nie mają szansy na łączenie się i wpływanie na siebie, ponieważ są stosowane do niezależnych zestawów osi.
Ten sam pomysł może pomóc, jeśli jest to obiekt na świecie, który chcemy obrócić. Na przykład taki jak kula ziemska, często chcielibyśmy go odwrócić i zastosować nasze odchylenie lokalnie (dzięki czemu zawsze obraca się wokół biegunów) i przechylać się globalnie (więc przechyla się w kierunku / z dala od naszego widoku, a nie w kierunku / z dala od Australii , gdziekolwiek wskazuje ...)
Ograniczenia
Ta globalna / lokalna strategia hybrydowa nie zawsze jest właściwym rozwiązaniem. Na przykład w grze z lotem / pływaniem 3D możesz chcieć być skierowany prosto w górę / prosto w dół i nadal mieć pełną kontrolę. Ale dzięki tej konfiguracji uderzysz w blokadę gimbala - twoja oś odchylenia (globalne w górę) staje się równoległa do osi przechyłu (lokalny do przodu) i nie możesz spojrzeć w lewo ani w prawo bez skręcania.
Zamiast tego w takich przypadkach możesz użyć czystych lokalnych rotacji, tak jak to rozpoczęliśmy w powyższym pytaniu (aby twoje kontrolki czuły to samo bez względu na to, gdzie patrzysz), co początkowo pozwoli, aby trochę się wkradło - ale potem poprawiamy to.
Na przykład, możemy użyć obrotów lokalnych, aby zaktualizować nasz wektor „do przodu”, a następnie użyć tego wektora do przodu wraz z referencyjnym wektorem „do góry”, aby skonstruować naszą ostateczną orientację. (Używając na przykład metody Quitynion.LookRotation firmy Unity lub ręcznie konstruując macierz ortonormalną z tych wektorów). Kontrolując wektor w górę, kontrolujemy rolkę lub skręcenie.
Na przykład w locie / pływaniu będziesz chciał stopniowo stosować te poprawki. Jeśli jest to zbyt gwałtowne, widok może się rozpraszać. Zamiast tego możesz użyć obecnego wektora w górę odtwarzacza i podpowiedzieć go w kierunku pionowym, klatka po klatce, aż jego poziom wyrówna się. Stosowanie tego podczas tury może czasami być mniej mdłości niż skręcanie kamery, gdy elementy sterujące odtwarzacza są bezczynne.
źródło
TCVector::calcRotationMatrix(totalPitch, totalYaw, identity)
- jest to odpowiednik powyższego przykładu „Euler naraz”.PO 16 godzinach funkcji rodzicielskich i rotacyjnych lol.
Chciałem tylko, żeby kod miał pusty, w zasadzie obracający się w kółko, a także obracający się z przodu.
Innymi słowy, celem jest obrócenie odchylenia i nachylenia bez żadnego wpływu na rzut.
Oto kilka, które faktycznie działały w mojej aplikacji
W jedności:
Teraz skryptuj w Visual Studio, przeprowadź konfigurację i zakończ to w Aktualizacji:
Zauważ, że wszystko się zepsuło, kiedy próbowałem zmienić kolejność rodzicielstwa. Lub jeśli zmienię Space.World na obiekt pitch dziecka (nadrzędny Yaw nie ma nic przeciwko obracaniu przez Space.Self). Mylące. Mógłbym zdecydowanie wyjaśnić, dlaczego musiałem przyjąć lokalną oś, ale zastosować ją w przestrzeni świata - dlaczego Space.Self rujnuje wszystko?
źródło
me.Rotate(me.right, angle, Space.World)
jest taka sama, jakme.Rotate(Vector3.right, angle, Space.Self
i twoje zagnieżdżone transformacje są równoważne przykładom „Pitch Lokalnie, Yaw Globalnie” w zaakceptowanej odpowiedzi.