OPENGL PRZEKSZTAŁCENIA GEOMETRYCZNE
SPIS TREŚCI 1. Przekształcenia geometryczne... 3 1.1. Obrót... 3 1.2. Skalowanie... 3 1.3. Przesunięcie... 3 1.4. Mnożenie macierzy... 4 1.5. Ładowanie macierzy... 4 1.6. Składanie przekształceń... 4 1.6.1. Kula... 5 1.6.2. Sześcian... 5 1.6.3. Stożek... 5 1.6.4. Torus... 6 1.6.5. Dwunastościan... 7 1.6.6. Czajnik... 7 1.6.7. Ośmiościan... 7 1.6.8. Czworościan... 7 1.6.9. Dwudziestościan... 8 1.7. Obsługa myszki... 8 1.8. Menu wielopoziomowe... 9 1.9. Odrysowanie okna... 9 1.10. Stos macierzy modelowania... 9 1.10.1. Operacje na stosie... 10
1. PRZEKSZTAŁCENIA GEOMETRYCZNE Obecnie poznamy mechanizmy biblioteki OpenGL umożliwiające dowolne przekształcenia geometryczne obiektów. Wykorzystamy także stos macierzy w celu modelowania sceny 3D zawierającej wiele elementów. 1.1. OBRÓT Obrót w OpenGL wykonują funkcje glrotated i glrotatef: void glrotated (GLdouble angle, GLdouble x, GLdouble y, GLdouble z) void glrotatef (GLfloat angle, GLfloat x, GLfloat y, GLfloat z) których parametry oznaczają: angle kąt obrotu w stopniach, x, y, z współrzędne wektora określającego oś obrotu. Obrót realizowany jest w kierunku przeciwnym do ruchu wskazówek zegara w kierunku prostej wyznaczonej przez wektor ( ) zaczepionym w początku układu współrzędnych. Jeżeli wektor ma długość różną od 1 wektor jest normalizowany. Funkcje glrotated i glrotatef mnożą bieżącą macierz przez macierz obrotu, która ma następującą postać (parametr angle zamieniono na a): ( ( ) ( ) ( ) ( ) ( ) ( ) ( ) ( ) ( ) ) 1.2. SKALOWANIE Skalowanie wykonują następujące funkcje: void glscalef (GLfloat x, GLfloat y, GLfloat z) void glscalex (GLfixed x, GLfixed y, GLfixed z) gdzie x, y, z są współczynnikami skalowania względem kolejnych osi układu współrzędnych. Funkcje te mnożą bieżącą macierz przez macierz skalowania, która ma postać: ( ) 1.3. PRZESUNIĘCIE Trzecią z podstawowych operacji geometrycznych jest przesunięcie o wektor (transalcja), którą realizują funkcje: void gltranslatef (GLfloat x, GLfloat y, GLfloat z) void gltranslatex (GLfixed x, GLfixed y, GLfixed z)
gdzie x, y, z są współrzędnymi wektora przesunięcia. Podobnie jak poprzed nio opisane funkcje, gltranslatef i gltranslatex mnożą bieżącą macierz przez macierz translacji, która ma postać: ( ) 1.4. MNOŻENIE MACIERZY W przypadku, gdy zestaw standardowych przekształceń macierzowych okazuje się niewystarczający, biblioteka OpenGL udostępnia możliwość przemnożenia bieżącej macierzy przez macierz określoną funkcją z grupy glmultmatrix: void glmultmatrixd (const GLdouble *m) void glmultmatrixf (const GLfloat *m) Parametr m obu funkcji zawiera wskaźnik na dane macierzy o rozmiarach 4 x 4, które muszą być ułożone kolejno kolumnami, tj. w kolejności transponowanej w stosunku do przyjętej w językach C i C++: ( ) 1.5. ŁADOWANIE MACIERZY Ostatnią z opisywanych operacji na macierzach jest zastępowanie bieżącej macierzy przez dowolnie określoną macierz o rozmiarach 4 x 4. Operację tę wykonują funkcje z grupy glloadmatrix: void glloadmatrixd (const GLdouble *m) void glloadmatrixf (const GLfloat *m) Format danych ładowanej macierzy jest taki sam jak w przedstawionych wyżej funkcjach glmultmatrixd i glmultmatrixf. 1.6. SKŁADANIE PRZEKSZTAŁCEŃ Macierzowy opis przekształceń geometrycznych znacznie ułatwia składanie kilku różnych przekształceń obiektów. Wywołując funkcje generujące macierze odpowiednich przekształceń dokonujemy jednocześnie ich składania. W tej sytuacji jedyną rzeczą, o której należy pamiętać, to kolejność przekształceń, bowiem od tego zależy końcowy efekt. Przykładowy program Przekształcenia przedstawia przekształcenia geometryczne dostępne w bibliotece OpenGL. Scena 3D zawiera pojedynczy obiekt geometryczny zawarty w bibliotece GLUT. Klawisze kursora pozwalają na obracanie wyświetlanego obiektu, przyciski + i - na jego skalowanie, a lewy przycisk myszki przesuwa obiekt. W ramach eksperymentu Czytelnik może w programie zamienić kolejność składania wykonywanych tam przekształceń (funkcja Display) np. przesunięcia i obrotu.
Poznajmy jeszcze nowe elementy biblioteki GLUT wykorzystanie w przykładowym programie tj. sposób obsługi myszki i zdefiniowane obiekty geometryczne. Biblioteka GLUT udostępnia obiekty 3D w wersji szkieletowej (tzw. druciaki ) oraz w wersji pełnej. W najbliższych przykładowych programach wykorzystamy wersje szkieletowe obiektów. 1.6.1. KULA Powierzchnia kuli rysowana jest jako szkielet składający się z południków i równoleżników (rysunek 1). Powierzchnię kuli, której środek znajduje się w początku układu współrzędnych rysuje funkcja: void glutwiresphere (GLdouble radius, GLint slices, GLint stacks) której parametry oznaczają: radius promień kuli, slices liczba południków, stacks liczba równoleżników. Rysunek 1. Program Przeksztalcenia - kula 1.6.2. SZEŚCIAN Sześcian o boku długości size i środku położonym w początku układu współrzędnych rysuje funkcja: void glutwirecube (GLdouble size) 1.6.3. STOŻEK
Stożek rysowany jest podobnie jak kula - jako szkielet oparty na równoległe do podstawy południki i tworzące biegnące od wierzchołka stożka do krawędzi jego podstawy (rysunek 2). Stożek ze środkiem podstawy umieszczonym w początku układu współrzędnych i wierzchołkiem umieszczonym na dodatniej półosi rysuje funkcja: void glutwirecone (GLdouble base, GLdouble height, GLint slices, GLint stacks) której parametry oznaczają: base promień podstawy stożka, height wysokość stożka, slices liczba tworzących, stacks liczba południków. Rysunek 2. Program Przekształcenia - stożek 1.6.4. TORUS Kolejna bryłą obrotową dostępną w bibliotece GLUT jest torus rysowany jako seria walców o nierównoległych podstawach (rysunek 3) i osi obrotu pokrywającej się z osią. Torus rysuje funkcja: void glutwiretorus (GLdouble innerradius, GLdouble outerradius, GLint sides, GLint rings) której parametry oznaczają odpowiednio: innerradius promień koła tworzącego torus, outerradius promień torusa,
sides liczba ścian bocznych, z których składa się pojedynczy walec, rings liczba walców, z których składa się torus. Rysunek 3. Program Przekształcenia - torus 1.6.5. DWUNASTOŚCIAN Dwunastościan o środku położonym w początku układu współrzędnych rysuje funkcja: void glutwiredodecahedron (void) 1.6.6. CZAJNIK Klasycznym obiektem 3D dostępnym w bibliotece GLUT jest opracowany w 1975 roku przez Martina Newella czajnik - przedstawiony na rysunku 4. Czajnik o wielkości regulowanej parametrem size rysuje funkcja: void glutwireteapot (GLdouble size) 1.6.7. OŚMIOŚCIAN Ośmiościan o środku położonym w początku układu współrzędnych rysuje funkcja: void glutwireoctahedron (void) 1.6.8. CZWOROŚCIAN Czworościan o środku położonym w początku układu współrzędnych rysuje funkcja: void glutwiretetrahedron (void)
Rysunek 4. Program Przekształcenia czajnik 1.6.9. DWUDZIESTOŚCIAN Dwudziestościan o środku położonym w początku układu współrzędnych rysuje funkcja: void glutwireicosahedron (void) 1.7. OBSŁUGA MYSZKI Obsługa myszki składa się z dwóch etapów i jest wykonywana przez dwie funkcje. Pierwsza z nich to: void MouseButton (int button, int state, int x, int y) która wykrywa naciśnięcie i zwolnienie lewego przycisku myszki. W zależności od stanu lewego przycisku myszki ustawiana jest wartość zmiennej globalnej button_state na przekazaną przez parametr state. Druga funkcja: void MouseMotion (int x, int y) wywoływana jest podczas ruchu kursora myszki a jej zadaniem jest odpowiednie (dobrane częściowo doświadczalnie) przeliczenie ruchu myszki na przesunięcie obiektu znajdującego się na scenie. Efekt przeliczeń ruchu kursora myszki umieszczony jest w zmiennych globalnych translatex i translatey, które są bezpośredni przekazywane jako parametry funkcji gltranslatef.
Parametry x i y obu funkcji oznaczają współrzędne kursora myszki w odniesieniu do układu współrzędnych okna, które oczywiście nie mają nic wspólnego ze współrzędnymi określonymi w scenie 3D. Parametr button funkcji MouseButton określa który przycisk myszki został naciśnięty lub zwolniony. Parametr ten przyjmuje jedną z wartości: GLUT_LEFT_BUTTON lewy przycisk myszki, GLUT_MIDDLE_BUTTON środkowy przycisk myszki, GLUT_RIGHT_BUTTON prawy przycisk myszki. Jeżeli w danym systemie myszka ma tylko dwa przyciski, wartość GLUT_MIDDLE_BUTTON nie będzie generowana. Natomiast w przypadku myszki z jednym przyciskiem funkcja generuje jedynie wartość GLUT_LEFT_BUTTON. Ostatni nieopisany parametr funkcji MouseButton to state, który określa czy przycisk myszki został naciśnięty (stała GLUT_UP) czy zwolniony (stała GLUT_- DOWN). Aby opisane funkcje obsługi myszki działały należy je dołączyć do listy funkcji zwrotnych wywołując odpowiednio: void glutmousefunc (void (*func)(int button, int state, int x, int y)) void glutmotionfunc (void (*func)(int x, int y)) 1.8. MENU WIELOPOZIOMOWE W programie został użyty nowy element biblioteki GLUT wielopoziomowe menu podręczne. Część menu podręcznego, które będzie znajdowało się na drugim (lub wyższym) poziomie tworzymy w taki sam sposób jak dotychczas tworzyliśmy główne menu, czyli przy użyciu funkcji glutcreatemenu i glutaddmenuentry. Tak utworzony fragment menu umieszczamy w menu głównym (nadrzędnym) używając funkcji: void glutaddsubmenu (const char *label, int submenu) Parametr label określa nazwę menu podręcznego, a submenu jego identyfikator zwrócony przez funkcję glutcreatemenu. 1.9. ODRYSOWANIE OKNA Aby zmiany sceny 3D zostały wyświetlone na ekranie potrzebne jest odrysowanie bieżącego okna. Można to zrealizować wywołując funkcję: void glutpostredisplay (void) 1.10. STOS MACIERZY MODELOWANIA Do składania przekształceń biblioteka OpenGL wykorzystuje wspomniany już wcześniej stos macierzy modelowania. W dotychczasowych programach mechanizm stosu nie był faktycznie wykorzystywany. Wszystkie operacje związane ze stosem macierzy modelowania wykonywane były na macierzy położonej na szczycie stosu. Pozwala to jednak na efektywną manipulację jedynie jednym obiektem na scenie. Oczywiście obsługa wielu obiektów także jest możliwa, ale wymaga to stosowania mniej wygodnych i efektywnych mechanizmów. W szczególności mogło by się okazać konieczne przechowywanie macierzy modelowania - czyli dublowanie możliwości oferowanej przez mechanizm stosu.
Po lekturze powyższego wstępu Czytelnik zastanawia się zapewne jak w praktyce efektywnie można wykorzystać mechanizm stosu macierzy modelowania. Klasycznym przykładem jest budowa obiektów złożonych z wielu obiektów podstawowych (np. takich jak wyżej opisane obiekty 3D dostępne w bibliotece GLUT). Drugim, równie często wykorzystywanym przykładem, jest animacja sceny składającej się z wielu obiektów 3D. 1.10.1. OPERACJE NA STOSIE Specyfikacja biblioteki OpenGL przewiduje, że stos macierzy modelowania przechowuje co najmniej 32 macierze. Oczywiście konkretne implementacje OpenGL mogą dowolnie zwiększać maksymalną pojemność tego stosu. Opisane poniżej funkcje służą do operacji na każdym rodzaju stosu dostępnym w OpenGL. Operacja zawsze zostanie wykonana na aktualnie wybranym rodzaju stosu. Operację odłożenia bieżącej macierzy na stos realizuje funkcja: void glpushmatrix (void) Natomiast operację zdjęcia macierzy ze stosu wykonuje funkcja: void glpopmatrix (void) Próba wykonania niedozwolonej operacji na stosie spowoduje wygenerowanie kodu błędu zwracanego przez funkcję glgeterror: GL_STACK_UNDERFLOW próba pobrania elementu z pustego stosu (niedobór stosu), GL_STACK_OVERFLOW przepełnienie stosu. Przykładowy program StosModelowania opiera się na przykładzie Przekształcenia. Podstawową różnicą jest rysowanie jednego obiektu - piramidy, ale złożonego z dużej ilości elementów podstawowych - sześcianów. Dla ułatwienia pracy program zawiera dwie funkcje rysujące części piramidy w postaci bloków 3 x 3 (funkcja Cube3x3) i bloków 2 x 2 (funkcja Cube2x2). Pierwszy poziom piramidy ma rozmiary 6 x 6 i jest rysowany z czterech bloków 3 x 3 (funkcja Pyramid). Przed narysowaniem każdego z elementów pierwszego poziomu macierz modelowania odkładana jest na stos - po zakończeniu rysowania następuje zdjęcie macierzy ze stosu. Przy rysowaniu drugiego i trzeciego poziomu piramidy następuje dwukrotne odłożenie macierzy modelowania na stos. Po pierwszym przesuwamy układ współrzędnych od odpowiednią liczba jednostek do góry. Drugie w kolejności odkładanie macierzy na stos wiąże się już bezpośrednio z rysowaniem kolejnych elementów danego poziomu piramidy. Rysowanie kolejnych poziomów nie wymaga już dodatkowego opisu - wystarcza jednokrotne odłożenie macierzy modelowania na stos. Przykładowy efekt tych wszystkich przekształceń zawiera rysunek 5. Oczywiście uzyskanie takiej figury nie wymaga stosowania mechanizmu stosu macierzy modelowania. Jednak już w tak prostym przykładzie widoczne są korzyści wynikające z takiego rozwiązania. Po pierwsze wszystkie elementy obiektu 3D, w tym wypadku poziomy piramidy, są od siebie niezależne. Czytelnik może to łatwo sprawdzić odpowiednio modyfikując tekst źródłowy programu. Trudno nie docenić takiej cechy przy tworzeniu bardziej skomplikowanych obiektów. Drugą zaletą jest duża przejrzystość rozwiązania, co ułatwia późniejszą modyfikację programu i usuwanie ewentualnych błędów. Warto także dodać, że mechanizm stosu jest bardzo szybki i dotyczy to wszystkich implementacji biblioteki OpenGL, nie tylko sprzętowych. W programie wykorzystano także kilka innych nieopisanych wcześniej funkcji biblioteki OpenGL i GLU. W poprzednim przykładzie przy przesuwaniu obiektu za pomocą myszki obliczanie wektora przesunięcia oparto o dobrane doświadczalnie współczynniki. W przypadku, gdy zmienimy parametry
bryły odcinania te współczynniki trzeba będzie dobierać ponownie. Biblioteka GLU zawiera funkcje przeliczające współrzędne w przestrzeni okna na współrzędne w przestrzeni widoku i odwrotnie. Rysunek 5. Program Stos modelowania Przeliczenie współrzędnych w przestrzeni widoku (objx, objy, objz) na współrzędne w przestrzeni okna (winx, winy, winz) wykonuje funkcja gluproject: GLint gluproject (GLdouble objx, GLdouble objy, GLdouble objz, const GLdouble modelmatrix [16], const GLdouble projmatrix [16], const GLint viewport [4], GLdouble *winx, GLdouble *winy, GLdouble *winz) Do przeliczania współrzędnych funkcja gluproject wykorzystuje macierz modelowania, macierz rzutowania oraz współrzędne okna renderingu, które zawarte są kolejno w parametrach: modelmatrix, projmatrix i viewport. Przeliczenie współrzędnych w przestrzeni okna (winx, winy, winz) na współrzędne w przestrzeni widoku (objx,objy,objz) wykonuje funkcja gluunproject:
GLint gluunproject (GLdouble winx, GLdouble winy, GLdouble winz, const GLdouble modelmatrix [16], const GLdouble projmatrix [16], const GLint viewport [4], GLdouble *objx, GLdouble *objy, GLdouble *objz) której parametry mają analogiczne znaczenie jak parametry poprzedniej funkcji gluproject. W wersji 1.3 biblioteki GLU dodano funkcję gluunproject4: GLint gluunproject4 (GLdouble winx, GLdouble winy, GLdouble winz, GLdouble clipw, const GLdouble modelmatrix[16], const GLdouble projmatrix[16], const GLint viewport[4], GLdouble near, GLdouble far, GLdouble *objx, GLdouble *objy, GLdouble *objz, GLdouble *objw) stanowiącą rozszerzenie gluunproject umożliwiającą obliczenia przy niestandardowych ustawieniach bufora głębokości (parametry near i far odpowiadające parametrom funkcji gldepthrange) lub w przypadku, gdy czwarta współrzędna (parametr clipw) przestrzeni okna przyjmuje inną wartość niż 1. Funkcja zwraca dodatkowo wartość czwartej współrzędnej w przestrzeni widoku (parametr objw). Znaczenie tych dodatkowych współrzędnych poznamy w kolejnych odcinkach kursu. Wartości macierzy modelowania, macierzy rzutowania oraz obszaru renderingu stanowią zmienne stanu maszyny stanu OpenGL. Do odczytu zmiennych stanu służy bardzo liczna grupa funkcji glget, z których najbardziej uniwersalne są następujące funkcje: void glgetbooleanv (GLenum pname, GLboolean *params) void glgetdoublev (GLenum pname, GLdouble *params) void glgetfloatv (GLenum pname, GLfloat *params) void glgetintegerv (GLenum pname, GLint *params) Parametr pname określa którą wartość maszyny stanów OpenGL chcemy pobrać (tabela wszystkich możliwych wartości zajmuje 35 stron specyfikacji OpenGL 2.1), a params wskaźnik na zwracaną wartość. W zależności od rodzaju pobieranej wartości params wskazuje na pojedynczą zmienną lub tablicę. Rodzaj zwracanej (zwracanych) wartości jednoznacznie określa końcowa część nazwy funkcji.