Notifications
Article
Struktura Quaternion – AngleAxis(), ToAngleAxis(), Angle(), Inverse(), eulerAngles, Euler(), operator *
Published 21 days ago
18
0
Struktura Quaternion – AngleAxis(), ToAngleAxis(), Angle(), Inverse(), eulerAngles, Euler(), operator *
To druga część artykułu na temat quaternionów.

Metoda AngleAxis()

Ta metoda pozwala utworzyć quaternion na bazie podanej (bezwzględnej) osi obrotu i kąta o jaki obracamy wokół tej osi.
float angle; Vector3 axis; // Tworzymy quaternion na podstawie znajomości osi obrotu i kąta Quaternion q = Quaternion.AngleAxis (angle: angle, axis: axis);
Tu znajdziecie skrypt, który pozwoli wam przetestować tworzenie quaternionów z pomocą tej metody.
Za pomocą parametru axis określamy kierunek osi obrotu. Za pomocą parametru angle określamy kąt o jaki obracamy ciało.

Mini uwaga!

Parametr axis w metodzie AxisAngle() reprezentuje bezwzględny kierunek osi obrotu (czyli v z poniższego wzoru ) bez uwzględniania sinusa połowy kąta obrotu.

Metoda ToAngleAxis()

Ta metoda pozwala na odczytanie z quaternionu względnego kierunku osi obrotu i kąta o jaki się obracamy (czyli ta metoda działa odwrotnie niż metoda AngleAxis())
//Do tych pól zostanie zwrócona informacja na temat osi obrotu i kąta obrotu Vector3 axis; float angle; // Z tego quaternionu chcemy uzyskać informacje na temat osi i kąta obrotu Quaternion q; //odczytujemy wartość osi obrotu i kąta obrotu, i zwracamy je do zmiennej axis oraz angle q.ToAngleAxis (angle:out angle, axis: out axis);
Tu znajdziecie skrypt, który pozwoli wam przetestować odczytywanie osi i kata obrotu quaternionów. Przykładowy quaternion możecie zdefiniować z pomocą kątów eulera, które zostaną zamienione na quaternion.
Na poniższym filmie możecie zobaczyć jak zmienia się kąt (angle) i względna oś obrotu (axis) wraz ze zmianą wartości quaternionu (wyrażonego w postaci kątów Eulera).
Zwróćcie uwagę jeszcze na zakres kątów zwracanych z pomocą tej metody. Metoda ToAngleAxis() zwraca kąty z przedziału 0-360° .

Mini uwaga!

Parametr axis w metodzie ToAxisAngle() reprezentuje względny kierunek osi obrotu (czyli bezwzględny kierunek osi obrotu wymnożony przez sinus połowy kąta obrotu).

Metoda Angle()

Ta metoda służy do odczytu wartości kąta obrotu między dwoma rotacjami.
//quaterniony reprezentujące rotacje między, którymi mierzymy kąt // te quaterniony będą parametrami w metodzie Angle() Quaternion a; Quaternion b; //kolejność quaternionów podawanych jako parametry nie ma znaczenia dla tej metody float angle = Quaternion.Angle (a: a, b: b);
Tu znajdziecie skrypt, który pozwoli wam przetestować odczyt kąta obrotu. Przykładowy quaterniony A i B możecie zdefiniować z pomocą kątów eulera, które zostaną zamienione na quaterniony.
Na poniższym filmie możecie zobaczyć jak zmienia się kąt (angle) wraz ze zmianą wartości quaternionów (wyrażonych w postaci kątów Eulera).

Zauważcie dwie rzeczy.
Kąt zmienia wartości tylko w zakresie 0° -180° . Czyli zwracany jest najmniejszy kąt między dwoma orientacjami (czyli quaternionem a i b).
Druga sprawa - zwracany jest bezwzględny kąt. Nie jesteśmy w stanie określić kierunku obrotu na podstawie znaku przy kącie.

Rozszerzenia związane z metodą Angle() i AngleAxis()

Utworzyłem metody rozszerzenia o nazwie SignedAngle() i SignedToAngleAxis(), które zwracają wartości kąta ze znakiem wskazującym kierunek obrotu. Zwracane kąty są z zakresu (-180,180).
Te metody możecie znaleźć w skrypcie QuaternionExtensions.



Metoda Inverse()

Ta metoda pozwala nam uzyskać quaternion odwrotny.
// quaternion, którego odwrotność chcemy uzyskać Quaternion q; // podajemy quaternion q jako parametr metody Inverse() // dzięki czemu uzysujemy quaternion odwrotny Quaternion qInverse = Quaternion.Inverse (rotation:q);

Czym jest quaternion odwrotny?

Przyjrzyjmy się wartościom parametrów quaterniona odwrotnego.
Tu znajduje się skrypt, który wyświetla wartość quaternionu oraz jego odwrotność.

Gdy przyjrzymy się bliżej parametrom quaternionu i jego odwrotności możemy zauważyć, że różnią się one tylko znakiem przy składowej x, y, z czyli kierunkiem (bezwzględnej) osi obrotu.
Metoda Inverse() wymnaża oś obrotu bazowego quaternionu razy -1. Na poniższym rysunku widać, że po wykonaniu operacji Inverse() oś obrotu skierowana jest w przeciwnym kierunku. Wpłynie to na kierunek obrotu. Parametr "w " się nie zmienił więc nadal obracamy się o taki sam kąt ale w przeciwnym kierunku bo obracamy się wokół osi skierowanej w przeciwnym kierunku niż dla bazowego quaternionu.

Kierunek obrotu wokół osi wynika z reguły lewej dłoni o której wspominałem w pierwszej części artykułu.
Zmieniając kierunek osi na przeciwny zmieniamy też kierunek obrotu na przeciwny.
Dzięki metodzie Inverse() możemy uzyskiwać quaterniony o przeciwnym kierunku obrotu. Przykłady użycia tej metody pokaże wam przy omawianiu operatora " * " w dalszej części artykułu.

Matematyka dla zuchwałych stojąca za metodą Inverse()

Na koniec wspomnę kilka słów na temat wzorów służących do obliczania quaternionu odwrotnego.
Załóżmy, że przykładowy quaternion o nazwie "q" opisujemy następującym wzorem:
Działanie quaternionów jest oparte o liczby zespolone. (W bibliografii znajdziecie dokładny opis matematyczny quaternionów, ja sobie ten opis darowałem bo nie jest niezbędny do korzystania z quaternionów)
Aby uzyskać quaternion odwrotny pierw musimy uzyskać sprzężenie quaternionu "q":

https://pl.wikipedia.org/wiki/Sprz%C4%99%C5%BCenie_zespolone
Następnie dzielimy i sprzężenie quaternionu przez kwadrat długości quaternionu.
W ten sposób matematycznie obliczamy quaternion odwrotny.

Właściwość eulerAngles i metoda Euler()

Jednym ze sposobów tworzenia quaternionów jest przekształcenie rotacji w postaci kątów Eulera na quaternion.
Do tej konwersji służy metoda Euler() i właściwość eulerAngles.
Kąty eulera reprezentują obroty wokół osi globalnego układu współrzędnych podobnie jak ma to miejsce w komponencie Transform.
Więcej na temat tematyki kątów Eulera mówiłem w tym odcinku przy omawianiu komponentu Transform.
Orientacje w postaci kątów Eulera możemy przekształcić na quaternion za pomocą statycznej metody Euler(). Ta metoda tworzy instancje quaternionu
// ten wektor przechowuję informacje o kątach obrotu wokół poszczególnych osi wyrażone w stopniach. Vector3 eulerAngles = new Vector3 (45,0,0); // uzyskujemy quaternion na bazie kątów eulera Quaternion q = Quaternion.Euler(euler: new Vector3 (45,0,0));
Możemy też zmienić orientacje istniejącego quaternionu z pomocą właściwości eulerAngles.
Quaternion q = Quaternion.identity; //przypisumemy nową rotacje do quaternionu q z pomocą właściwości eulerAngles q.eulerAngles = new Vector3 (45,0,0);
W gameObjecie Euler i eulerAngles znajdziecie przykładowe skrypty wykorzystujące metodę Euler() i właściwość eulerAngles do tworzenia quaternionów.

Właściwość eulerAngles pozwala także na odczyt quaternionu i przedstawienie go w postaci kątów Eulera. Poszczególne składowe kąta będą przyjmować wartości z przedziału 0° -360° .
Quaternion exampleQuaternion; // odczytujemy quaternionów i przedstawiamy je w postaci kątów Eulera czyli w postaci // obrotów w stopniach wokół osi obrotu Vector3 readOrientationAsEulerAngles = exampleQuaternion.eulerAngles;

Operator *

W strukturze Quaternion znajduję się operator " * " który pozwala na wykonywanie operacji mnożenia dwóch quaternionów lub quaternionu i wektora.

Mnożenie quaternionów

Operator * pozwala na wymnażanie przez siebie quaternionów. Takie mnożenie quaternionów traktujemy jako kolejno wykonane obroty ciała.
Poniżej przykład mnożenia quaternionów.
// A jest bazową rotacją Quaternion A; // wymnażamy A przez rotacje B Quaternion B; // mnożymy od prawej do lewej gdy rotacje reprezentują obroty wokół osi globalnego układu współrzędnych // Poniższy zapis interpretujemem w następujący sposób: ciało o rotacji A obracamy o rotacje B. Quaternion endRotation = B * A;

Kolejność mnożenia quaternionów z pomocą operatora *.

Gdy obracamy ciała należy pamiętać, że kolejność obracania ma wpływ na końcową rotacje ciała. Czyli jeśli obrócimy ciało najpierw o rotacje A a potem jeszcze o B to otrzymamy inną rotacje niż gdybyśmy pierw obrócili o B a potem o A. Mnożenie quaternionów nie jest przemienne.
A * B ≠ B * A
Poniżej możecie zobaczyć, jak będzie różnić się końcowa rotacja ciała w zależności od kolejności obracania ciała.
Aby zachować odpowiednią kolejność obracania ciała musimy w odpowiedniej kolejności wymnażać quaterniony.

Przykładowy przebieg procesu mnożenia quaternionów.

Quaterniony mnożymy od prawej do lewej strony (gdy rotacje reprezentują obroty wokół osi globalnego układu współrzędnych).
Czyli quaternion znajdujący się najdalej po prawo jest naszą bazową rotacją od której zaczynamy mnożenie quaternionu, np. może być to bazowa rotacja ciała przechowywana w komponencie Transform. Quaternion najbardziej po prawej reprezentuje początkową rotacje ciała, które chcemy obracać.
Jak wygląda kolejno proces mnożenia quaternionów:
  1. Quaternion A (czyli startowa rotacja ciała) jest obracany o rotacje B
  2. Otrzymaną rotacje obracamy o rotacje C
  3. Otrzymaną rotacje obracamy o rotacje D i tym samym otrzymujemy ostateczną rotacje ciała.
Podczas mnożenia quaternionów możemy je łączyć w grupy co ułatwia interpretacje procesu ich mnożenia.


W jakiej kolejności są wykonywane obroty wokół poszczególnych osi układu współrzędnych gdy korzystamy z kątów Eulera?

Kąty Eulera reprezentują rotacje wokół osi globalnego układu współrzędnych czyli obrót wokół globalnej osi z, x i y.
Kolejność obrotów potrzebna do uzyskania kątów Eulera jest ściśle narzucona. Aby otrzymać ostateczną rotacje musimy wykonać obroty wokół globalnych osi w następującej kolejności:
  1. Obracamy pierw wokół osi z globalnego układu współrzędnych
  2. Potem obracamy wokół osi x globalnego układu współrzędnych
  3. Na końcu obracamy wokół y globalnego układu współrzędnych
// obróty wokół poszczególnych osi // w tym przykładzie obracam o 45 wokół każdej z globalnych osi Quaternion rotationAroundGlobalX = Quaternion.AngleAxis(45, Vector3.right); Quaternion rotationAroundGlobalY = Quaternion.AngleAxis(45, Vector3.up); Quaternion rotationAroundGlobalZ = Quaternion.AngleAxis(45, Vector3.forward); // sumaryczna rotacja, mnożenie zaczynamy od prawej // pierw obracamy wokół osi Z, potem wokół osi X, a na końcu wokół osi Y Quaternion fullRotation = rotationAroundGlobalY * rotationAroundGlobalX * rotationAroundGlobalZ; //powyższy zapis jest równoważny temu zapisowi Quaternio fullRotation = Quaternion.Euler(45,45,45);
Poniżej przygotowałem wizualizacje, pokazującą końcową rotacje ciała w zależności od wyboru kolejności obrotu. Wyniki przeglądam w widoku Sceny z ustawieniem na 2D aby perspektywa nie zmieniała pozycji osi.
Zobaczcie jaki wpływ ma zła kolejność wyboru osi na końcową rotacje ciała, gdy w złej kolejności wymnożymy kąty Eulera.

Metody, które wykorzystują kąty Eulera

Cześć z metod wykorzystuję kąty Eulera jako parametry. W ich opisach możecie znaleźć np. informacje o tym wokół jakich osi się obracamy i kolejności obrotu wokół osi:
Applies a rotation of eulerAngles.z degrees around the z-axis, eulerAngles.x degrees around the x-axis, and eulerAngles.y degrees around the y-axis (in that order).
Ten opis pochodzi z opisu metody Transform.Rotate()
https://docs.unity3d.com/ScriptReference/Transform.Rotate.html
Ta informacja odnosi się do tego o czym mówiłem w poprzednim podrozdziale i wskazuję jednocześnie, że obrót następuję wokół osi globalnego układu współrzędnych.
W niektórych przypadkach rotacja może być zdefiniowana także względem "rodzica" więc zwracajcie uwagę na tę informacje.

Potencjalne problemy z animacjami

W Unity kąty Eulera są definiowane jako obroty kolejno wokół osi ZXY.
Inne programy mogą inaczej definiować kąty Eulera tym samym obroty wokół osi mogą następować w innej kolejności.
Mogą zdarzyć się sytuacje, że importowane modele będą miały zdefiniowane inaczej niż w Unity kąty Eulera. Tu w dokumentacji znajdziecie więcej informacji na ten temat.
https://docs.unity3d.com/Manual/AnimationEulerCurveImport.html

Obroty wokół osi lokalnego układu współrzędnych ciała

W niektórych sytuacjach będziemy chcieli obracać ciała wokół osi lokalnego układu współrzędnych (a nie globalnych osi). We wcześniejszych przykładach pokazywałem wam kolejność rotacji gdy rotacje są zdefiniowane jako obroty wokół osi globalnego układu współrzędnych.
Gdy obracamy wokół osi lokalnego układu współrzędnych obroty musimy wykonać w nieco innej kolejności. Wszystko zależy od wybranej wersji zapisu.
// Tworzymy 3 rotacje wokół osi globalnego ukł. współrzędnych Quaternion X = Quaternion.AngleAxis(45, Vector3.up) * Quaternion Y = Quaternion.AngleAxis(45, Vector3.right) * Quaternion Z = Quaternion.AngleAxis(45, Vector3.forward); // zauważcie, że w tym przypadku bazowa rotacja znajduję się najbardziej z lewej strony // dzięki czemu "przekształcamy" globalne rotacje na lokalne // pierw wymnożymy bazową rotacje przez rotacje Y // potem uzyskaną rotacje wymnażamy przez X // potem uzyskaną rotacje wymnażamy przez Z transform.rotation = transform.rotation * Y * X * Z; POWYŻSZE OBLICZENIA MOŻEMY ZAPISAĆ TAKŻE W PONIŻSZY SPOSÓB // tworzymy rotacje złożoną z rotacji wokół globalnych osi // wymnożonych w kolejności ZXY Quaternion exampleRotation = Y * X * Z; // wymnażamy bazową rotacje przez exampleRotation // kolejność mnożenia będzie następująca: // bazową rotacje wymnażamy przez exampleRotation // kolejność mnożenia sprawi, że exampleRotation będzie traktowane jako mnożenie w kolejności YXZ a nie ZXY transform.rotation = transform.rotation * exampleRotation;
Zauważcie, że w przypadku obrotów wokół lokalnych osi mnożymy od lewej do prawej. Położenie bazowej rotacji określa czy będziemy obracać wokół globalnych czy lokalnych osi obrotu.
Jeśli bazowa rotacja jest najbardziej z prawej to obracamy wokół globalnych osi.
Jeśli bazowa rotacja jest z lewej to obracamy wokół lokalnych osi.

Inne zapisy służące do obrotu wokół osi lokalnego układu współrzędnych

Oprócz wcześniejszych zapisów możecie także skorzystać z poniższych notacji do obrotu wokół lokalnych osi:
// uzyskujemy rotacje wokół lokalnych osi Quaternion X = Quaternion.AngleAxis(45, transform.right); Quaternion Y = Quaternion.AngleAxis(45, transform.up); Quaternion Z = Quaternion.AngleAxis(45, transform.forward); // dzięki temu, że posiadamy rotacje wokół lokalnych osi, możemy wymnażać w tej kolejności // od prawej do lewej czyli ja w przypadku obrotów wokół globalnych osi transform.rotation = Y * X * Z * transform.rotation; //LUB... // Możemy obrócić wokół lokalnych osi także w ten sposób Quaternion exampleRotation = Y * X * Z; transform.rotation = exampleRotation * transform.rotation;

Operator *=

Uważajcie gdy korzystacie z operatora *= do mnożenia quaternionów.
Quaternion A,B; // możemy wymnożyć dwa quaterniony w taki sposób A*=B; // co będzie równoważne takiemu zapisowi A= A*B;
Głównym problemem gdy korzystamy z operatora *= jest kolejność mnożenia quaternionów. Możemy korzystać z tego operatora gdy będziemy chcieli obracać wokół osi lokalnego układu współrzędnych.
Aby uniknąć problemów ze złą kolejnością korzystajcie z pełnego zapisu mnożenia quaternionów.
Quaternion A,B; //zamiast korzystać z tego zapisu w którym łatwo pomylić kolejność obrotów A*=B; // korzystajcie z pełnego zapisu mnożenia quaternionów // aby zapewnić właściwą kolejność mnożenia quaternionów w zależności od potrzeb A = A*B; LUB A=B*A;

Jak matematycznie zdefiniowane jest mnożenie quaternionów.

Teraz pokaże wam wzór, który służy do obliczania iloczynu dwóch quaternionów, który kryje się za operatorem " * ". Załóżmy, że mamy dwa quaterniony p i q zdefiniowane w następujący sposób:
Obracamy quaternion p o quaternion q. Iloczyn dwóch quaterionów możecie obliczyć z pomocą następującego wzoru:
Poniżej przykład kodu do liczenia tego iloczynu:
// obracamy quaternion p o quaternion q Quaternion p; Quaternion q; // na potrzeby dalszych obliczeń pierw uzyskujemy wektory osi obrotu quaternionów Vector3 qAxis = new Vector3(q.x, q.y, q.z); Vector3 pAxis = new Vector3(p.x, p.y, p.z); // oraz wartości parametru w float qw = q.w; float pw = p.w; // tak wygląda wzór służący do obliczenia wektora osi obrotu dla quaternionu będącego wynikiem mnożenia Vector3 pqAxis = qw * pAxis + pw * qAxis + Vector3.Cross(qAxis, pAxis); // tak wygląda wzór służący do obliczenia wartości parametru w dla quaternionu będącego wynikiem mnożenia float pqw = qw * pw - Vector3.Dot(qAxis, pAxis); // na koniec ze składowych tworzymy nowy quaternion // przypominam, że obracamy quaternion p o quaternion q Quaternion pq = new Quaternion(pqAxis.x, pqAxis.y, pqAxis.z, pqw);
Możemy też obrócić quaternion p o quaternion q z pomocą omawianego operatora *, co zmniejsza ilość obliczeń:
// obracamy quaternion p o quaternion q Quaternion p; Quaternion q; // korzystanie z operatora * ułatwia wykonanie obliczeń Quaternion pq = q * p;

Przykłady mnożenia quaternionów

Pokaże wam kilka przykładowych sytuacji mnożenia quaternionów abyście lepiej zrozumieli ten proces.

Sytuacja 1

Mamy dwa gameObjecty w relacji rodzic i dziecko. Chcemy uzyskać lokalną i globalną rotacje dziecka. Globalną rotacje dziecka obliczamy ze wzoru:
GlobalnaRotacjaDziecka = GlobalnaRotacjaRodzica + LokalnaRotacjaDziecka;
Quaternion parentGlobalRotation; Quaternion childLocalRotation; // Globalną rotacje dziecka wyliczamy w następujacy sposób: // zaczynamy od prawej strony // childLocalRotation obracamy o parentGlobalRotation i uzyskujemy globalną rotacje dziecka Quaternion childGlobalRotation = parentGlobalRotation * childLocalRotation;
Przekształćmy wcześniejszy wzór aby uzyskać lokalną rotacje:
LokalnaRotacjaDziecka= GlobalnaRotacjaDziecka - GlobalnaRotacjaRodzica;
Quaternion parentGlobalRotation; Quaternion childGlobalRotation; //Lokalną rotacje dziecka wyliczamy w następujacy sposób: // zaczynamy od prawej strony // childGlobalRotation wymnażamy przez odwrotność parentGlobalRotation // Metoda Quaternion.Inverse() pozwala uzyskać przeciwną rotacje co jest równoważne "odejmowaniu" tej rotacji Quaternion childLocalRotation = Quaternion.Inverse(parentGlobalRotation) * childGlobalRotation;
Metoda Quaternion.Inverse() bardzo się przydaje gdy chcecie odejmować od siebie rotacje. Przeanalizujcie dokładnie ten przykład!

Sytuacja 2

Obróciliśmy ciało o jakiś quaternion i chcemy powrócić do bazowej rotacji.
Quaternion q = Quaternion.Euler(45,45,45); // obracamy o jakiś quaternion transform.rotation = q * transform.rotation; // jeśli chcemy wrócić do rotacji przed obrotem należy wymnożyć przez odwrotność quaternionu transform.rotation = Quaternion.Inverse(q) * transform.rotation;
Wynika to z tego, że:
Quaternion q = Quaternion.Euler(45,45,45); // wynikiem wymnożenie quaternionu przez jego odwrotność jest Quaternion.identity Quaternion p = q * Quaternion.Inverse(q);

Sytuacja 3

Przeanalizujmy działanie metody Transform.Rotate().
Ta metoda pozwala na obrót ciała o określony kąt wyrażony w kątach Eulera. Możemy obracać wokół osi lokalnego bądź globalnego układu współrzędnych.
Jeśli zajrzymy do kodu z pomocą ILSpy to zobaczymy, że obrót wokół lokalnych osi jest zdefiniowany w następujący sposób:
Quaternion rhs = Quaternion.Euler(eulers.x, eulers.y, eulers.z); // wymnażamy od lewej do prawej, bazową rotacje przez rhs this.localRotation *= rhs;
Jeśli chodzi o obroty wokół globalnych osi to jest to zdefiniowane w taki sposób:
Quaternion rhs = Quaternion.Euler(eulers.x, eulers.y, eulers.z); this.rotation *= Quaternion.Inverse(this.rotation) * rhs * this.rotation;
Zapewne zastanawia was ten dziwny zapis rotacji. Dokonajmy kilku przekształceń:
// Pierw skorzystamy z dłuższego zapisu this.rotation = this.rotation * Quaternion.Inverse(this.rotation) * rhs * this.rotation; // Zauważcie, że wymnażamy bazową rotacje przez jej odwrotność co będzie równoważne Quaternion.identity this.rotation = (this.rotation * Quaternion.Inverse(this.rotation)) * rhs * this.rotation; // tym samym cały zapis uprości się do postaci co reprezentuję obrót wokół globalnych osi this.rotation = rhs * this.rotation;

Obracanie wektorów o określony quaternion z pomocą operatora *

Operator " * " ze struktury quaternion pozwala na wymnażanie wektorów przez quaterniony. Taka operacja powoduje obrót wektora o określoną rotacje.
Poniżej przykład mnożenia wektora przez quaternionów.
// A jest wektorem, który będziemy obracać Vector3 A; // rotacje o którą będziemy obracać wektor Quaternion B; // wektor A jest obracany o rotacje B w wyniku czego zwracany jest obrócony wektor Vector3 rotatedA = B * A;
Zauważcie, że podobnie jak to miało miejsce w przypadku mnożenie quaternionów, wymnażamy od prawej do lewej. Czyli najbardziej z prawej musi znaleźć się wektor, który chcemy obrócić a z lewej strony operatora " * " musi się znaleźć quaternion o jaki obracamy wektor.
Tu macie wizualizacje. Vectro3.right wymnażamy przez quaternion utworzony przez metodę AngleAxis().

Wynikiem takiej operacji jest Vector3.right obrócony o zdefiniowaną rotacje.

Zła kolejność

Jeśli spróbujemy wymnożyć wektor i quaternion w odwrotnej kolejności wyświetli się błąd.
Vector3 A; Quaternion B; // zła kolejność mnożenia Vector3 rotatedA = A * B;
Tu macie treść błędu.

Jak matematycznie zdefiniowane jest mnożenie wektora przez quaternion?

Teraz pokaże wam wzór, który służy do obliczania iloczynu wektora i quaternionu, który kryje się za operatorem " * ".
Co jest czym w tym wzorze:
Poniższa implementacja w Unity pozwoli wam lepiej zrozumieć ten wzór.
// wektor, który chcemy obrócić Vector3 vector; Vector3 axis; float angle; // rotacja, o którą chcemy obrócić wektor Quaternion q = Quaternion.AngleAxis(angle, axis); //odwrotność q, potrzebna do dalszych obliczeń Quaternion qInverse = Quaternion.Inverse(q); // tworzymy "pure quaternion / vector quaternion" z wektora abyśmy mogli wymnożyć go z quaternionami // quaterniony możemy zdefiniować z pomocą liczb zespolonych, tak zdefiniowany quaternion będzie posiadał część reczywistą i urojoną, cześć rzeczywista będzie reprezentowana przez parametr "w" a urojona przez x, y, z // q = ix + jy + kz + w = (x,y,z, w) // "pure quaternion" to taki, w którym część rzeczywista jest równa 0, czyli w tym przypadku parametr w = 0 Quaternion vectorAsPureQuaternion = new Quaternion(vector.x, vector.y, vector.z, 0); // wymnażamy w takiej kolejności quaterniony i otrzymujemy nowy quaternion zawierający informacje o odwróconym wektorze Quaternion rotatedVectorAsQuaternion = q * vectorAsPureQuaternion * qInverse; // wyciągamy składową x,y,z z quaternionu i tworzymy z ich pomocą odwrócony wektor Vector3 rotatedVector = new Vector3(rotatedVectorAsQuaternion.x, rotatedVectorAsQuaternion.y, rotatedVectorAsQuaternion.z);
Powyższy zapis mnożenia wektora i quaternionu jest dość długi dlatego po prostu korzystajcie z operatora " * "
Vector3 v; Vector3 rotatedVector = q * v;










Tags:
Rafał Tadajewski
I haven't duck. I took dino. - Programmer
9
Comments