Jak mogę obrócić kamerę wokół jej punktu docelowego?

18

Rysuję scenę, w której kamera swobodnie porusza się po wszechświecie. Klasa kamery śledzi punkt widzenia (lub spojrzenie na ), pozycję kamery i wektor w górę. Te wektory / punkty są następnie przekazywane do gluLookAt.

Przesuwanie i powiększanie są prawie banalne do wdrożenia. Jednak widzę, że obrót o spojrzenie na punkcie być znacznie większy problem. Chcę napisać funkcję Camera.rotate, która ma 2 kąty, jeden, który obraca się w górę / w dół i jeden, który obraca się w lewo / prawo wzdłuż wyimaginowanej sfery, która jest wyśrodkowana wokół spojrzenia w punkcie.

Czy jest na to łatwy sposób?

Przeczytałem (krótko) o czwartorzędach, ale chciałem sprawdzić, czy istnieje łatwiejsze rozwiązanie, biorąc pod uwagę stosunkowo prostą konstrukcję mojej sceny.

Łukasz
źródło
Czy masz coś w przyborniku, aby obrócić punkt wokół początku, biorąc pod uwagę oś i kąt?
sam hocevar,
Nic poza moim luźnym rozumieniem Czwartorzędów, nie. Im dłużej na to patrzę, myślę, że Quaternions mogą być odpowiedzią. Nie jestem jednak pewien, jak obliczyć wartości osi x, y i z do użycia we wzorach czwartorzędu.
Łukasza,
Nie powiem wam, że czwartorzędy nie są dobrą drogą. Są bardzo dobrym rozwiązaniem twojego problemu. Ale będziesz potrzebować czwartorzędowej klasy i sposobów interakcji z GL i GLU, i uważam, że powinieneś najpierw spróbować zapoznać się z macierzami transformacji. Inni mogą się nie zgadzać.
sam hocevar,
zastanów się, w jakiej kolejności wykonujesz rotacje. stosowanie rotacji do przemieszczania się w przestrzeń świata lub do przenoszenia w przestrzeń kamery jest różne
Charlie

Odpowiedzi:

25

To, o co prosisz, nazywa się rotacją Arcball. Czwartorzędy są łatwym rozwiązaniem tylko wtedy, gdy zrozumiesz, jak działają. Możesz to samo osiągnąć bez czwartorzędów.

Warunki wstępne

Czy wiesz jak ogólnie obracać obiekty? Powiedzmy, że masz obiekt u źródła. Czy wiesz, jak go obracać (wskazówka: pomnóż przez jakąś macierz obrotu)? Jeśli tak, to zakładam, że wiesz, co się stanie, jeśli najpierw przetłumaczysz obiekt, a następnie go obrócisz?

Musisz wiedzieć, jak obliczyć macierz obrotu z osi kąta (proste jak ciasto, spójrz na mnóstwo równań online, wiele z nich daje również kod)

Rozwiązanie

  • Uzyskaj kamery w górę i odpowiednie wektory. Pamiętaj, że powinny zostać znormalizowane.
  • Przenieś wektor z punktu ostrości do aparatu (camPosition - Focus). To jest wektor, który będziesz obracać. Nazwijmy to camFocusVector .
  • Zdecyduj, ile chcesz obrócić w odchyleniu / skoku względem aparatu
  • Utwórz dwie macierze obrotu. Matryca pierwszego obrotu wykorzysta górną część kamery jako kąt osi i odchylenia , który zdecydowałeś. 2. macierz obrotu użyje prawo aparatu jak i osi pitch kątem, że zdecydowałeś.
  • Teraz obróć camFocusVector za pomocą nowych matryc obrotu. To jest teraz Twoja nowa pozycja kamery względem początku. Oczywiście chcemy, aby był on względny w stosunku do punktu skupienia ...
  • Dodaj pozycję punktu ostrości do camFocusVector . To jest teraz nowa pozycja twojego aparatu. Przetłumacz odpowiednio swój aparat.
  • Na koniec poproś kamerę, aby skupiła się na punkcie ostrości, wywołując funkcję lookAt ()

Ostrzeżenia

Będziesz musiał uważać na pewne przypadki lub osobliwości, w których aparat przestanie działać. Na przykład patrząc prosto w dół / w górę. Pozwolę ci dowiedzieć się, jak sobie z tym poradzić.

EDYCJA 1: Jak ponownie obliczyć wektory ortonormalne kamery

Znasz już kierunek kamery ((cameraPos - focusPoint) .normalize ()). Załóżmy teraz, że kamera jest skierowana w górę o + Y (lub w / e aktualna oś skierowana w górę na świecie to ... to zależy od ciebie). Teraz wystarczy przekroczyć kierunek z góry , aby uzyskać prawo . Gotowy? Nie! Twój wektor do góry nie jest już prostopadły do ​​pozostałych dwóch. Aby to naprawić, krzyż prawo z kierunku i masz swój nowy up .

Zauważ, że Gram-Schmidt jest naprawdę tym, co powinno być używane do ortonormalizacji wektorów.

Ponownie zwróć uwagę na zastrzeżenia, ponieważ w niektórych przypadkach to nie zadziała ( kierunek jest na przykład równoległy do góry ).

Samaursa
źródło
Zauważ, że prawy wektor można uzyskać za pomocą normalize(up ^ camFocusVector)(lub przeciwnie, jeśli leworęczny).
sam hocevar,
Do tej pory wyglądał dobrze, działał pięknie dla rotacji lewo / prawo (mam wektor do góry, więc to proste). Co oznacza „^” w twoim komentarzu @SamHocevar? Czy to produkt krzyżowy? Ponadto, jak mogę ponownie obliczyć wektor górny po wykonaniu tłumaczeń?
Łukasza,
@Luke: Sprawdź mój EDIT1
Samaursa,
1
@Samaursa Dziękuję bardzo. Twoje rozwiązanie działa idealnie i wiele się nauczyłem!
Łukasz
2
to jest DOSKONAŁA odpowiedź, bardzo dziękuję za wyjaśnienie. W moim przypadku chciałem, aby kamera obracała się tylko wokół punktu docelowego na płaszczyźnie XY, więc po wykonaniu wszystkich obliczeń zawsze ustawiałem cameraRight.z na 0, a następnie obliczałem wektor cameraUp. Dało to pożądany efekt. Pomyślałem tylko o udostępnieniu
klucz szyfrujący
1

Potrzebna jest typowa kamera ArcBall, która jest zasadniczo kamerą, która utrzymuje lokalizację docelową i pozwala przesuwać kamerę w „sferyczny” sposób wokół tego celu.

Jest w XNA, ale IIRC korzystałem już z tej implementacji i działał całkiem dobrze:

http://roy-t.nl/index.php/2010/02/21/xna-simple-arcballcamera/

David Gouveia
źródło
Wydaje się, że ta implementacja porusza się tylko wzdłuż prawego wektora, który przybliża arcball dla małych wartości kwoty . Przesuwa także punkt LookAt, gdzie chcę przesunąć kamerę (blisko, ale subtelnie inaczej). Wierzę, że Samaursa zapewnił najprostszy kompletny sposób na osiągnięcie tego.
Łukasz
Dziękujemy za twoją opinię. Muszę przyznać, że użyłem tej implementacji trochę jako „czarnej skrzynki”, ale nie zauważyłem żadnego problemu. Aby to wyjaśnić, być może mówiłeś o metodach MoveCameraRight / MoveCameraForward? Te metody zostały dodane w celu przesuwania kamery, ale nie są częścią interfejsu Arcball. Jeśli chcesz obrócić kamerę wokół celu, po prostu zmień właściwości Yaw lub Pitch.
David Gouveia,
Przepraszamy, masz rację, to są metody przesuwania. Nie zauważyłem, jak ustawił brudną flagę matrycy, gdy zmieniono odchylenie i skok.
Łukasz
0

Oto mój kod do obracania się wokół punktu, bardzo często go wykorzystuję.

float distance;      // Straight line distance between the camera and look at point

// Calculate the camera position using the distance and angles
float camX = distance * -sinf(camAngleX*(M_PI/180)) * cosf((camAngleY)*(M_PI/180));
float camY = distance * -sinf((camAngleY)*(M_PI/180));
float camZ = -distance * cosf((camAngleX)*(M_PI/180)) * cosf((camAngleY)*(M_PI/180));

// Set the camera position and lookat point
gluLookAt(camX,camY,camZ,   // Camera position
          0.0, 0.0, 0.0,    // Look at point
          0.0, 1.0, 0.0);   // Up vector
Stephen Tierney
źródło
1
Zasadniczo tak to zrobiłem na początku. Jest wiele problemów z robieniem tego w ten sposób (przynajmniej dla mnie). Zasadniczo ta implementacja obraca tylko około 2 stałych osi. Powiedzmy, że zbliżasz się do bieguna północnego . Następnie obróć w prawo . Przekręcisz się w małym kółku zamiast podążać za właściwym wektorem kamery na całym świecie .
Łukasz
Teraz, kiedy o tym wspomniałeś, myślę, że istnieją dwa różne sposoby spojrzenia na problem. W mojej aplikacji korzystałem z kamery ArcBall do obracania się wokół wyspy docelowej na morzu, a gdybym używał 3 osi swobody, wyglądałoby to zupełnie źle (na przykład widok wyspy do góry nogami lub na boki).
David Gouveia,
Jak uzyskać tutaj kąty kamery?
rafvasq
0

Ten bardzo prosty algorytm do osiągnięcia tego jest niesamowity:

Będąc P centralnym punktem, który chcesz obrócić (punkt „patrz” lub „cel”):

translate(-P)

rotate horizontal
rotate vertical

translate(P)

Użyłem go i jest fajny.

Źródło znalezione na stronie siostrzanej Stackoverflow: /programming/287655/opengl-rotating-a-camera-around-a-point

diosney
źródło