Przekształcenia geometryczne Dorota Smorawa
Przekształcenia geometryczne Na poprzednich laboratoriach już dowiedzieliśmy się, na czym polegają podstawowe przekształcenia geometryczne. Trzy podstawowe przekształcenia to obrót glrotate, przesunięcie gltranslate oraz skalowanie glscale. Są to typowe przekształcenia geometryczne, które korzystając z biblioteki OpenGL używamy w bardzo prosty sposób. W rzeczywistości biblioteka OpenGL dokonuje przemnożenia aktualnej macierzy modelowania przez macierz odpowiedniego przekształcenia, wyliczając w ten sposób nowe położenie dla określonych wierzchołków w przestrzeni kartezjańskiej: Jeśli chcemy przykładowo dokonać obrotu wokół osi y, to wywołujemy funkcję: glrotatef(alfa, 0.0f, 1.0f, 0.0f) //alfa to kąt obrotu Jeśli chcemy przykładowo dokonać przemieszczenia o wektor [1,1,0], to wywołujemy funkcję: gltranslatef(1.0f, 1.0f, 0.0f) Jeśli chcemy przykładowo dokonać pomniejszenia o połowę, to wywołujemy funkcję: glscalef(0.5f, 0.5f, 0.5f)
Stos macierzy Jeśli na naszej scenie znajduje się tylko jeden obiekt, na którym dokonujemy przekształceń wówczas w zasadzie wszelkie przekształcenia przebiegają pod całkowitą kontrolą. Wiemy co, w jaki sposób i o ile chcemy zmienić. Zupełnie inna sytuacja ma miejsce, kiedy na scenie mamy już przynajmniej dwa obiekty, na których możemy dokonywać operacje przekształceń geometrycznych. Jeśli nie zabezpieczymy naszej sceny, wówczas użyte przez nasz przekształcenia dotyczyć będą wszystkich obiektów. Chcemy obrócić tylko widoczny obiekt, a obróci nam się jeszcze kamera, podłoże, światło itd.. Na szczęście biblioteka OpenGL udostępnia mechanizm stosu macierzy. Stos macierzy pozwala na wykonanie przekształceń tylko dla obiektów, które na takim stosie umieścimy. Do umieszczenia macierzy na stosie służy funkcja glpushmatrix(), natomiast do zdejmowania ze stosu funkcja glpopmatrix();
Stos macierzy Jeśli na scenie mamy dwa obiekty i dla jednego chcemy zastosować obrót a dla drugiego przesunięcie, to powinniśmy zastosować stos macierzy dla obu przekształceń w następujący sposób: glpushmatrix() // umieszczenie macierzy na stosie glrotatef(30.0f, 0.0f, 1.0f, 0.0f); // obrót o kąt 30 stopni w prawo wokół osi y glutsolidtorus(0.2f, 0.4f, 80, 40); // narysowanie torusa glpopmatrix() // zdjęcie macierzy ze stosu glpushmatrix() // umieszczenie macierzy na stosie gltranslatef(10.0f, 0.0f, 20.0f); // przesunięcie o wektor [10,0,20] glutsolidsphere(0.2f, 17, 9); // narysowanie kuli glpopmatrix() // zdjęcie macierzy ze stosu
Macierz jednostkowa Dokonując jakiegokolwiek przekształcenia na obiekcie w rzeczywistości zmieniamy jego macierz modelowania. Każde kolejne przekształcenie wiąże się z tym, iż musimy pamiętać, co zrobiliśmy wcześniej i w jakim miejscu znajduje się obecnie obiekt. Jest to o tyle kłopotliwe, że przy wielu przekształceniach łatwo można się pogubić. W bibliotece OpenGL jest funkcja, która umożliwia powrót macierzy obiektu do stanu początkowego. Jest to tak zwana macierz jednostkowa glloadidentity(); Jeśli chcemy np. dokonać najpierw na obiekcie przesunięcia o wektor [0,1,0], a później o wektor [1,0,0] możemy uzyskać dwa różne położenia końcowe obiektu, w zależności od tego, czy po pierwszym przekształceniu powrócimy za pomocą macierzy jednostkowej do położenia początkowego, czy nie.
Macierz jednostkowa Przypadek bez użycia macierzy jednostkowej: glmatrixmode(gl_modelview); // wywołanie macierzy modelowania gltranslatef(0.0f, 1.0f, 0.0f); // przesunięcie o wektor [0,1,0] glutsolidsphere(0.2f, 17, 9); // narysowanie kuli gltranslatef(1.0f, 0.0f, 0.0f); // przesunięcie o wektor [1,0,0] glutsolidsphere(0.2f, 17, 9); // narysowanie kuli Przypadek z użyciem macierzy jednostkowej: glmatrixmode(gl_modelview); // wywołanie macierzy modelowania gltranslatef(0.0f, 1.0f, 0.0f); // przesunięcie o wektor [0,1,0] glutsolidsphere(0.2f, 17, 9); // narysowanie kuli glloadidentity(); // powrót do początkowego położenia gltranslatef(1.0f, 0.0f, 0.0f); // przesunięcie o wektor [1,0,0] glutsolidsphere(0.2f, 17, 9); // narysowanie kuli
Niezależne poruszanie obiektów Stosując stos macierzy oraz macierz jednostkową możemy w sposób niezależny poruszać każdym obiektem użytym na scenie z osobna. Jest to o tyle przydatne, gdyż najwięcej problemów pojawia się w momencie, gdy na scenie oprócz widocznych obiektów mamy jeszcze umieszczone oświetlenie oraz kamerę. Podstawowe przekształcenia geometryczne stosuje się zarówno do obiektów widocznych na scenie, jak również do obiektów wirtualnych, jak np. kamera, lub do wszelkiego rodzaju świateł. Stos macierzy pozwala na bezpieczne poruszanie się po scenie kamerą, światłem w sposób całkowicie kontrolowany. Możemy niezależnie poruszać każdym obiektem, biorącym czynny udział w scenie 3D
Pojęcie aktora i widza Modelowanie scen 3D wnosi ze sobą kilka pojęć, których nie można pominąć. Tworząc scenę 3D tworzymy obiekty widoczne, dodajemy oświetlenie, dodajemy kamerę. Każdy z tych elementów nosi w modelowaniu 3D swoją odpowiednią dla przeznaczenia nazwę. Należy tutaj wspomnieć o dwóch podstawowych pojęciach, aktora i widza. Aktorzy, to wszystkie obiekty widoczne na scenie, biorące czynny udział w budowaniu sceny. Nie chodzi tu wyłącznie o ludzkie postaci, aktorzy to drzewa, samochody, ludzie, zwierzęta itd. Są też aktorzy, którzy w sposób bierny oddziałują na scenę, to np. różnego rodzaju oświetlenie. Pojęcie widza można utożsamiać z obserwatorem sceny, użytkownikiem gry, symulatora itd. Do tego pojęcia właśnie należy zaliczyć obiekty zwane kamerami, które definiują punkt widzenia i sposób poruszania się obserwatora.
Kamera Kamera utożsamiana jest z widzem, obserwatorem sceny 3D. Możemy obserwować scenę z jednej kamery, możemy przełączać się pomiędzy wieloma kamerami, możemy poruszać się po scenie. Ruch kamery po scenie wiąże się z modyfikacją przestrzeni widocznej, a w zasadzie przestrzeni kartezjańskiej zdefiniowanej dla aktorów tej sceny. Krok do przodu, to przesunięcie całego świata w kierunku dodatnim współrzędnej opisującej głębię. W bibliotece OpenGL nie ma takiego obiektu, jak kamera. Kamera to pojęcie obiektu wirtualnego, którego zachowanie możemy zaprogramować. Aby zdefiniować ruch kamery po scenie możemy wykorzystać funkcję definiującą przekształcenie punktu widzenia glulookat.
Klawisze specjalne W podanym przykładzie pojawiła się funkcja Funkcja_klawisze_specjalne. Funkcja ta odpowiada za wykorzystywanie klawiszy specjalnych z klawiatury, m.in. Strzałek, klawiszy funkcyjnych itp. W funkcji tej parametrami nie są znaki, a specjalnie zdefiniowane nazwy klawiszy specjalnych. Wywołanie tej funkcji odbywa się w funkcji main w następujący sposób: glutspecialfunc (Funkcja_klawisze_specjalne);
Pomocnicza biblioteka Oprócz bibliotek podstawowych dostępnych jest sporo pomocniczych bibliotek, które ułatwiają życie programistom scen 3D. Taką biblioteką jest biblioteka pomoc, która została udostępniona. Biblioteka ta ma w sobie zdefiniowany ruch kamery po scenie. Oczywiście taką kamerę należy w odpowiedni sposób zainicjować: #include "pomoc/glpomoc.h // dołączenie biblioteki GLPFrame framecamera; // obiekt typu GLPFrame //Inicjacja kamery w funkcji sceny glpinitframe (&framecamera); // wywołanie kamery w funkcji sceny glpapplycameratransform (&framecamera);
Pomocnicza biblioteka Ruch kamery po scenie do góry i na dół: void Funkcja_klawiatury (unsigned char key, int x, int y) { switch (key) { case 'u': glpmoveframeup (&framecamera, 0.2f); break; case 'd': glpmoveframeup (&framecamera, -0.2f); break; } //odswiezenie okna glutpostredisplay (); }
Pomocnicza biblioteka Ruch kamery po scenie zdefiniowany klawiszami strzałek: void Funkcja_klawisze_specjalne (int key, int x, int y) { switch (key) { case GLUT_KEY_LEFT: glprotateframelocaly(&framecamera, 0.1); break; case GLUT_KEY_RIGHT: glprotateframelocaly(&framecamera, -0.1); break; case GLUT_KEY_UP: glpmoveframeforward(&framecamera, 0.1f); break; case GLUT_KEY_DOWN: glpmoveframeforward(&framecamera, -0.1f); break; } //odswiezenie okna glutpostredisplay (); }
Opis funkcji glpushmatrix Zapisuje aktualną macierz na stos macierzy. Najczęściej wykorzystywana jest do zapisywania aktualnej macierzy przekształceń. void glpushmatrix (void); *********************************************************************************** glpopmatrix Zdejmuje aktualną macierz ze stosu macierzy void glpopmatrix (void); *********************************************************************************** glpinitframe parametry Inicjacja kamery na scenie void glpinitframe (GLPFrame *pframe); *pframe układ odniesienia kamery
Opis funkcji glpapplycameratransform Dodanie kamery do sceny void glpapplycameratransform (GLPFrame *Camera); parametry *pcamera układ odniesienia kamery *********************************************************************************** glprotateframelocalx Funkcja dokonująca obrotu kamery wokół osi x void glprotateframelocalx (GLPFrame *pframe, GLfloat fangle); parametry *pframe układ odniesienia kamery fangle kąt obrotu *********************************************************************************** glprotateframelocaly Funkcja dokonująca obrotu kamery wokół osi y void glprotateframelocaly (GLPFrame *pframe, GLfloat fangle); parametry *pframe układ odniesienia kamery fangle kąt obrotu
Opis funkcji glprotateframelocalz Funkcja dokonująca obrotu kamery wokół osi z void glprotateframelocalz (GLPFrame *pframe, GLfloat fangle); parametry *pframe układ odniesienia kamery fangle kąt obrotu *********************************************************************************** glpmoveframeforward Funkcja realizująca ruch kamery w pozycji przód tył void glpmoveframeforward (GLPFrame *pframe, GLfloat fstep); parametry *pframe układ odniesienia kamery fstep wartość kroku
Opis funkcji glpmoveframeup Funkcja realizująca ruch kamery w pozycji góra dół void glpmoveframeup (GLPFrame *pframe, GLfloat fstep); parametry *pframe układ odniesienia kamery fstep wartość kroku *********************************************************************************** glpmoveframeright Funkcja realizująca ruch kamery w pozycji lewo prawo void glpmoveframeright (GLPFrame *pframe, GLfloat fstep); parametry *pframe układ odniesienia kamery fstep wartość kroku
Opis funkcji glptranslateframelocal Funkcja realizująca przesunięcie kamery do określonego miejsca void glptranslateframelocal (GLPFrame *pframe, GLfloat x, GLfloat y, GLfloat z); parametry *pframe układ odniesienia kamery x, y, z współrzędne nowej pozycji kamery
Literatura 1. Richard S. Wright jr, Michael Sweet: OpenGL Księga eksperta Wydanie III, Helion2005 2. Pomoce dydaktyczne i instrukcje, http://icis.pcz.pl/~mkubanek 3. Jackie Neider, Tom Davis, Mason Woo: OpenGL Programming Guide The RedBook 4. Kevin Hawkins, Dave Astle: OpenGL. Programowanie gier, Helion 2003 5. The official OpenGL web page, http://www.opengl.org 6. http://januszg.hg.pl/opengl/