Prezentowane materiały są przeznaczone dla uczniów szkół ponadgimnazjalnych. Autor artykułu: mgr Jerzy Wałaszek, wersja1.0 |
©2011 mgr
Jerzy Wałaszek
|
Typ danych w OpenGL | Wewnętrzna reprezentacja | Odpowiednik w C++ | Sufiks literowy funkcji |
---|---|---|---|
GLbyte | liczba całkowita 8-bitow | signed char | b |
GLshort | liczba całkowita 16-bitów | short int | s |
GLint, GLsizei | liczba całkowita 32-bity | long int | I |
GLfloat, GLclampf | liczba zmiennoprzecinkowa 32-bity | float | f |
GLdouble, GLclampd | liczba zmiennoprzecinkowa 64-bity | double | d |
GLubyte, GLboolean | liczba całkowita bez znaku 8-bitów | unsigned char | ub |
GLushort | liczba całkowita bez znaku 16-bitów | unsigned short int | us |
GLuint, GLenum, GLbitfield | liczba całkowita bez znaku 32-bitów | unsigned long int | ui |
clamp pochodzi od color amplitude (amplituda koloru) i jest stosowane w funkcjach definiujących składowe koloru.
GLboolean stosowane jest do wartości logicznych.
Sufiks literowy funkcji służy do oznaczania typów argumentów. Poznaliśmy dotychczas kilka funkcji, które stosują tę cechę:
glColor3f() - funkcja posiada 3 argumenty typu GLfloat
...
Nazwy wszystkich typów rozpoczynają się od GL, aby wskazać ich przynależność do OpenGL.
Biblioteka | Nazwa pliku | Plik nagłówkowy | Prefiks funkcji |
---|---|---|---|
Pomocnicza | glaux.lib | glaux.h | aux |
OpenGL | opengl32.dll | gl.h | gl |
Użytkowa | glu32.dll | glu.h | glu |
Prefiks funkcji określa jej przynależność do danej biblioteki. Dotychczas używaliśmy funkcji z biblioteki OpenGL. Wszystkie posiadały przedrostek gl:
//--------------------------------------------------------------------------- void __fastcall Tfrm3D::FormResize(TObject *Sender) { glViewport(0, 0, ClientWidth, ClientHeight); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(45.0f,(GLfloat)ClientWidth/(GLfloat)ClientHeight,0.1f,300.0f); glMatrixMode(GL_MODELVIEW); glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // kolor tła glEnable(GL_DEPTH_TEST); // włącza bufor głębokości glDepthFunc(GL_LESS); glEnable(GL_CULL_FACE); // włącza opcję eliminacji ścian glFrontFace(GL_CW); // ściany o wierzchołkach ułożonych zgodnie z ruchem wskazówek // zegara będą traktowane jako zwrócone przodem glCullFace(GL_BACK); // pomija rysowanie ścian odwróconych tyłem } //---------------------------------------------------------------------------
Na początku mamy wywołanie funkcji glViewport(). Jej parametry definiują prostokąt widoku na obszarze okna, w którym będzie rysowana scena (możesz mieć w oknie kilka widoków sceny, np. z różnych kierunków).
void glViewport(GLint x, GLint y, GLsizei width, GLsizei height);
Prostokąt ten rozpoczyna się w punkcie o współrzędnych x,y i posiada długość width oraz wysokość height. W naszym przypadku prostokąt widoku obejmuje cały obszar okna aplikacji.
Druga funkcja glMatrixMode(GL_PROJECTION) wybiera do działań macierz projekcji, czyli macierz obserwatora. Macierz ta określa położenie obserwatora oraz sposób patrzenia na scenę.
glLoadIdentity() umieszcza w macierzy projekcji macierz jednostkową. Jak pamiętamy z poprzednich zajęć, macierz jednostkowa jest przekształceniem tożsamościowym, tzn. nie zmienia widoku sceny (to tak jak w mnożeniu 1, które nie zmienia wyniku mnożenia). Macierz jednostkową ładujemy zawsze wtedy, gdy chcemy rozpocząć transformacje od początku.
Kolejna funkcja gluPerspective() należy do biblioteki użytkowej. Ustala ona sposób prezentacji sceny w prostokącie widoku, który ustawiła funkcja glViewport(). W tym przypadku zostaje wybrany widok perspektywiczny. Cechuje się on tym, iż przedmioty znajdujące się bliżej obserwatora stają się większa, a przedmioty znajdujące się dalej stają się mniejsze. Funkcja mnoży macierz projekcji przez macierz perspektywy. Ponieważ wcześniej ustawiliśmy w macierzy projekcji macierz jednostkową (którą możemy potraktować jak w mnożeniu jedynkę), to w efekcie w macierzy projekcji pojawi się teraz macierz perspektywy.
Kolejne wywołanie funkcji glMatrixMode(GL_MODELVIEW) wybiera do działań macierz widoku obiektu, co przenosi nas do drugiego etapu, który będzie realizowany w funkcji obsługi zdarzenia onTimer. Macierz widoku obiektu jest używana do przekształceń układu współrzędnych sceny. Macierz projekcji jest jakby punktem startowym tych przekształceń.
Aby zilustrować te rozważania, stwórzmy prosty program. Najpierw wpisz w funkcji obsługi zdarzenia onResize następujący kod:
//--------------------------------------------------------------------------- void __fastcall Tfrm3D::FormResize(TObject *Sender) { glViewport(0, 0, ClientWidth, ClientHeight); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(45.0f,(GLfloat)ClientWidth/(GLfloat)ClientHeight,0.1f,300.0f); glTranslatef(0.0f,0.0f,-6.0f); // przesuwamy początkowy układ w tył o 6 jednostek glMatrixMode(GL_MODELVIEW); glClearColor(0.0f, 0.0f, 0.0f, 0.0f); glEnable(GL_DEPTH_TEST); // włącza bufor głębokości glDepthFunc(GL_LEQUAL); glEnable(GL_CULL_FACE); // włącza opcję eliminacji ścian glFrontFace(GL_CW); // ściany o wierzchołkach ułożonych zgodnie z ruchem wskazówek // zegara będą traktowane jako zwrócone przodem glCullFace(GL_BACK); // pomija rysowanie ścian odwróconych tyłem } //---------------------------------------------------------------------------
Zwróć uwagę, że transformacji dokonaliśmy tutaj w macierzy projekcji. Zatem początkowy widok naszego układu współrzędnych będzie taki, iż układ ten jest cofnięty o 6 jednostek. Transformacji tych nie musimy już wykonywać w układzie obiektu. Kolejny kod wklej do funkcji obsługi zdarzenia onTimer. Kod rysuje obracający się sześcian.
// Tutaj umieszczamy program dla OpenGL static GLfloat alpha = 0; // kąt obrotu glRotatef(alpha,1.0f,1.0f,1.0f); // wykonujemy tylko obrót układu współrzędnych alpha += 1; if(alpha > 360) alpha = 0; glBegin(GL_QUAD_STRIP); // cztery ściany boczne glColor3f(1.0f,0.0f,0.0f); glVertex3f(-1.0, 1.0, 1.0); glVertex3f(-1.0, 1.0,-1.0); glColor3f(1.0f,1.0f,0.0f); glVertex3f( 1.0, 1.0, 1.0); glVertex3f( 1.0, 1.0,-1.0); glColor3f(0.0f,1.0f,0.0f); glVertex3f( 1.0,-1.0, 1.0); glVertex3f( 1.0,-1.0,-1.0); glColor3f(0.0f,0.0f,1.0f); glVertex3f(-1.0,-1.0, 1.0); glVertex3f(-1.0,-1.0,-1.0); glColor3f(1.0f,0.0f,0.0f); glVertex3f(-1.0, 1.0, 1.0); glVertex3f(-1.0, 1.0,-1.0); glEnd(); glBegin(GL_QUADS); // przód i tył glColor3f(1.0f,0.0f,0.0f); glVertex3f(-1.0, 1.0, 1.0); glColor3f(1.0f,1.0f,0.0f); glVertex3f( 1.0, 1.0, 1.0); glColor3f(0.0f,1.0f,0.0f); glVertex3f( 1.0,-1.0, 1.0); glColor3f(0.0f,0.0f,1.0f); glVertex3f(-1.0,-1.0, 1.0); glColor3f(0.0f,1.0f,0.0f); glVertex3f( 1.0,-1.0,-1.0); glColor3f(1.0f,1.0f,0.0f); glVertex3f( 1.0, 1.0,-1.0); glColor3f(1.0f,0.0f,0.0f); glVertex3f(-1.0, 1.0,-1.0); glColor3f(0.0f,0.0f,1.0f); glVertex3f(-1.0,-1.0,-1.0); glEnd(); // Koniec kodu dla OpenGL
W funkcji FormResize() umieść komentarz przed wywołaniem gluPerspective() i dodaj pod spodem wpis:
glOrtho(-2.0f,2.0f,-2.0f,2.0f,0.0f,8.0f);
Funkcja glOrto() umieści w macierzy projekcji macierz rzutu prostokątnego. W rzucie prostokątnym wymiary przedmiotów nie zmieniają się w zależności od odległości od obserwatora. Parametry są następujące:
void glOrtho(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble nearVal, GLdouble farVal);
left, right – określają współrzędne pionowych płaszczyzn obcinania. Płaszczyzny te znajdują się odpowiednio przy lewej i prawej krawędzi prostokąta widoku.
bottom, top – współrzędne dolnej i górnej poziomej płaszczyzny obcinania. Płaszczyzny te znajdą się na dolnej i górnej krawędzi prostokąta widoku.
nearVal – określa odległość od obserwatora, poniżej której ściany nie będą rysowane.
farVal – określa odległość od obserwatora, powyżej której ściany nie będą rysowane.
Gdy uruchomisz teraz nasz program, perspektywa zniknie.
Pomimo tych wad, rzut prostokątny jest czasem przydatny. Sprawdź co się dzieje, gdy zmieniasz wymiary swojego okna, np. rozciągnij je w poziomie lub w pionie.
Z projektu usuń funkcję glOrto() i przywróć gluPerspective().
Gdy tak zdefiniowana ściana odwraca się tyłem do obserwatora, kolejność wierzchołków zmienia się na przeciwną do ruchu wskazówek zegara. Gdy włączymy ukrywanie ścian odwróconych tyłem, OpenGL przed narysowaniem ściany szybko sprawdzi sobie kolejność jej wierzchołków. Jeśli będzie przeciwna do ruchu wskazówek zegara, to ściana nie zostanie narysowana. Przy bardzo skomplikowanej scenie opcja ta przyspiesza tworzenie rysunku.
Opcja ukrywania ścian w naszym programie jest włączana w funkcji obsługi zdarzenia onResize. Aby to zadziałało prawidłowo, należy wywołać trzy funkcje:
glEnable(GL_CULL_FACE) | – | jest to ogólna funkcja uaktywniająca określoną parametrem opcję w OpenGL. GL_CULL_FACE włącza ukrywanie ścian. Opcję tę wyłączasz za pomocą funkcji glDisable(GL_CULL_FACE). Standardowo opcja jest wyłączona. |
glFrontFace(GL_CW) | – | ta funkcja informuje OpenGL, iż ściany zwrócone przodem do obserwatora stosują kolejność wierzchołków zgodną z ruchem wskazówek zegara. Parametr GL_CW określa tę kolejność. Możliwa jest również kolejność odwrotna do ruchu wskazówek zegara GL_CCW. Standardowo jest ustawiony kierunek odwrotny do ruchu wskazówek zegara, czyli GL_CCW. |
glCullFace(GL_BACK) | – | ta funkcja określa, które ściany mają być
pomijane. Parametr GL_BACK powoduje pomijanie ścian, które są
odwrócone tyłem. Możliwe są jeszcze dwie kolejne opcje:
GL_FRONT - nie będą rysowane ściany odwrócone przodem do
obserwatora |
Pobaw się chwilę programem obracającym sześcian, stosując różne opcje w wymienionych powyżej funkcjach.
//--------------------------------------------------------------------------- void __fastcall Tfrm3D::FormResize(TObject *Sender) { glViewport(0, 0, ClientWidth, ClientHeight); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(45.0f,(GLfloat)ClientWidth/(GLfloat)ClientHeight,0.1f,300.0f); glTranslatef(0.0f,0.0f,-6.0f); // przesuwamy początkowy układ w tył o 6 jednostek glMatrixMode(GL_MODELVIEW); glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // glEnable(GL_DEPTH_TEST); // włącza bufor głębokości glDepthFunc(GL_LEQUAL); // glEnable(GL_CULL_FACE); // włącza opcję eliminacji ścian glFrontFace(GL_CW); // ściany o wierzchołkach ułożonych zgodnie z ruchem wskazówek // zegara będą traktowane jako zwrócone przodem glCullFace(GL_BACK); // pomija rysowanie ścian odwróconych tyłem } //---------------------------------------------------------------------------
Do eliminacji tego niepożądanego efektu OpenGL wykorzystuje tzw. bufor głębokości. Polega on na tym, iż z każdym pikselem ekranu skojarzona jest liczba przechowywana w buforze głębokości. Liczba ta określa "odległość" tego piksela w przestrzeni 3D od obserwatora. Na początku tworzenia sceny bufor głębokości jest ustawiany tak, iż piksele ekranu są w największej możliwej odległości od obserwatora. Rysując ścianę, OpenGL wylicza odległość każdego jej piksela. Jeśli w buforze dla tego piksela jest wartość mniejsza, to piksel jest już zajęty przez ścianę leżącą bliżej obserwatora, zatem nowy piksel nie będzie rysowany. W przeciwnym razie piksel jest rysowany, a w buforze zostaje umieszczona jego odległość od obserwatora. Dzięki temu ściany leżące dalej nie będą przesłaniały ścian już narysowanych bliżej.
Aby OpenGL stosowało bufor głębokości, należy włączyć tę opcję za pomocą wywołania:
glEnable(GL_DEPTH_TEST);
Następnie należy określić warunki rysowania piksela za pomocą wywołania funkcji:
glDepthFunc(warunek);
Warunków jest wiele:
GL_NEVER | – | piksele nigdy nie są rysowane, ściany się nie pojawią |
GL_LESS | – | piksel jest rysowany, jeśli jego odległość jest mniejsza od odległości w buforze. Jest to standardowa wartość. |
GL_EQUAL | – | piksel jest rysowany, jeśli jego odległość jest równa odległości w buforze. |
GL_LEQUAL | – | piksel jest rysowany, jeśli jego odległość jest mniejsza lub równa odległości w buforze. |
GL_GREATER | – | piksel jest rysowany, jeśli jego odległość jest większa od odległości w buforze. |
GL_NOTEQUAL | – | piksel jest rysowany, jeśli jego odległość różni się od odległości w buforze. |
GL_GEQUAL | – | piksel jest rysowany, jeśli jego odległość jest większa lub równa odległości w buforze. |
GL_ALWAYS | – | piksel jest zawsze rysowany. |
Na początku funkcji obsługi zdarzenia onTimer mamy wywołanie funkcji:
Zeruje ona bufor koloru (powierzchnia graficzna robi się czarna, gdyż taki kolor tła wybrano funkcją glClearColor(0.0f, 0.0f, 0.0f, 0.0f) w onResize – spróbuj zmienić kolor tła na inny) oraz ustawia bufor głębokości.
Kolejny kod rysuje trzy sześciany wirujące wokół wspólnej osi. Dodatkowo w obróconym układzie sześciany wirują kolejno wokół poszczególnych osi układu współrzędnych. Poeksperymentuj na nim z włączaniem/wyłączaniem bufora głębokości. Próbuj zbliżać lub oddalać od siebie sześciany, modyfikując przesunięcia za pomocą glTranslatef(). Również spróbuj wyłączyć funkcję glClear() przez umieszczenie przed nią komentarza.
// Tutaj umieszczamy program dla OpenGL static GLfloat alpha = 0; // kąt obrotu glRotatef(alpha,1.0f,1.0f,1.0f);// wykonujemy tylko obrót układu współrzędnych glTranslatef(-3.0f,0.0f,0.0f); // na pozycję pierwszego sześcianu alpha += 1; if(alpha > 360) alpha = 0; for(int i = 0; i < 3; i++) { glRotatef(alpha,(i==0),(i==1),(i==2)); // obracamy układ współrzędnych glBegin(GL_QUAD_STRIP); glColor3f(1.0f,0.0f,0.0f); glVertex3f(-1.0, 1.0, 1.0); glVertex3f(-1.0, 1.0,-1.0); glColor3f(1.0f,1.0f,0.0f); glVertex3f( 1.0, 1.0, 1.0); glVertex3f( 1.0, 1.0,-1.0); glColor3f(0.0f,1.0f,0.0f); glVertex3f( 1.0,-1.0, 1.0); glVertex3f( 1.0,-1.0,-1.0); glColor3f(0.0f,0.0f,1.0f); glVertex3f(-1.0,-1.0, 1.0); glVertex3f(-1.0,-1.0,-1.0); glColor3f(1.0f,0.0f,0.0f); glVertex3f(-1.0, 1.0, 1.0); glVertex3f(-1.0, 1.0,-1.0); glEnd(); glBegin(GL_QUADS); glColor3f(1.0f,0.0f,0.0f); glVertex3f(-1.0, 1.0, 1.0); glColor3f(1.0f,1.0f,0.0f); glVertex3f( 1.0, 1.0, 1.0); glColor3f(0.0f,1.0f,0.0f); glVertex3f( 1.0,-1.0, 1.0); glColor3f(0.0f,0.0f,1.0f); glVertex3f(-1.0,-1.0, 1.0); glColor3f(0.0f,1.0f,0.0f); glVertex3f( 1.0,-1.0,-1.0); glColor3f(1.0f,1.0f,0.0f); glVertex3f( 1.0, 1.0,-1.0); glColor3f(1.0f,0.0f,0.0f); glVertex3f(-1.0, 1.0,-1.0); glColor3f(0.0f,0.0f,1.0f); glVertex3f(-1.0,-1.0,-1.0); glEnd(); glRotatef(-alpha,(i==0),(i==1),(i==2)); glTranslatef(3.0f,0.0f,0.0f); } // Koniec kodu dla OpenGL
Animacja z buforem głębokości | Animacja bez bufora głębokości i bez czyszczenia okna. |
Czworościan foremny (tetraedr) jest figurą posiadającą cztery identyczne ściany będące trójkątami równobocznymi. Na początek zajmijmy się prostym przypadkiem, gdy nasz tetraedr jest wpisany w sześcian:
Przypadek ten jest prosty, ponieważ wierzchołki sześcianu można bardzo łatwo wyznaczyć. Jeśli umówimy się, że środek układu współrzędnych znajduje się w środku sześcianu, a sześcian ma boki o długości 1, to współrzędne x, y i z kolejnych wierzchołków są następujące:
v0 = ( 0,5 -0,5 0,5) v1 = (-0,5 0,5 0,5) v2 = ( 0,5 0,5 -0,5) v3 = (-0,5 -0,5 -0,5)
Gdy znamy już punkty wierzchołkowe, możemy określić każdą ścianę jako ciąg trzech wierzchołków podanych w kolejności zgodnej z ruchem wskazówek zegara (kolejność ta jest istotna, jeśli w programie włączyliśmy ukrywanie ścian odwróconych tyłem do obserwatora):
S0 = (v0 v1 v2)
S1 = (v1 v3 v2)
S2 = (v3 v0 v2)
S3 = (v0 v3 v1)
Taką figurę możemy określić za pomocą trzech tablic:
S - tablica definiująca ściany trójkątne. Każda ściana zawiera 3 liczby całkowite będące numerami kolejnych wierzchołków (zgodnie z ruchem wskazówek zegara).
C - tablica definiująca kolory ścian. Każdy element zawiera 3 składowe R, G i B koloru ściany.
Po tych ustaleniach możemy przystąpić do napisania odpowiedniego kodu. Na początku programu umieść następujący kod:
//--------------------------------------------------------------------------- void Tetraedr(GLfloat v[][3], int s[][3], GLfloat c[][3]) { glBegin(GL_TRIANGLES); // ściany trójkątne for(int i = 0; i < 4; i ++) // kolejne ściany { glColor3fv(c[i]); // kolor i-tej ściany for(int j = 0; j < 3; j++) glVertex3fv(v[ s[i][j] ]); } glEnd(); } //---------------------------------------------------------------------------
W funkcji Tetraedr() zastosowaliśmy nowe funkcje do definicji koloru oraz wierzchołka:
glColor3fv(adres);
glVertex3fv(adres);
Parametrem tych funkcji jest adres obszaru pamięci, w którym przechowywane są kolejno 3 liczby typu GLfloat będące wartościami r, g, b lub współrzędnymi x, y i z wierzchołka. Użycie tych funkcji upraszcza nasz kod. Jeśli mamy tablicę dwuwymiarową, np. T[4][3], to pierwszy indeks określa rząd w tej tablicy. Element T[i] jest adresem i-tego wiersza. Wykorzystujemy to w funkcjach, przekazując jako parametr i-ty wiersz tablicy kolorów lub wiersz tablicy wierzchołków o numerze przechowywanym w s[i][j], a to jest numer j-tego wierzchołka, który tworzy ścianę.
Zmień kod w obsłudze Timera:
// Tutaj umieszczamy program dla OpenGL // Definiowanie figury // Wierzchołki static GLfloat V[][3] = {{ 0.5f,-0.5f, 0.5f}, // v0 {-0.5f, 0.5f, 0.5f}, // v1 { 0.5f, 0.5f,-0.5f}, // v2 {-0.5f,-0.5f,-0.5f}}; // v3 // Ściany static int S[][3] = {{0,1,2}, // S0 {1,3,2}, // S1 {3,0,2}, // S2 {0,3,1}}; // S3 // Kolory ścian static GLfloat C[][3] = {{1.0f,0.0f,0.0f}, // kolor S0, czerwony {0.0f,1.0f,0.0f}, // kolor S1, zielony {0.0f,0.0f,1.0f}, // kolor S2, niebieski {1.0f,1.0f,0.0f}}; // kolor S3, żółty static GLfloat alphax = 0; // kat obrotu static GLfloat alphay = 0; // kat obrotu static GLfloat alphaz = 0; // kat obrotu glTranslatef(0.0f,0.0f,-3.0f); glRotatef(alphax,1.0f,0.0f,0.0f); glRotatef(alphay,0.0f,1.0f,0.0f); glRotatef(alphaz,0.0f,0.0f,1.0f); Tetraedr(V,S,C); // kąt dla następnej klatki animacji alphax += 0.4; if(alphax > 360) alphax = 0; alphay += 1.0; if(alphay > 360) alphay = 0; alphaz += 0.6; if(alphaz > 360) alphaz = 0; // Koniec kodu dla OpenGL
Funkcję rysującą obiekt możemy w prosty sposób uogólnić na dowolną ilość ścian trójkątnych. Również kolory mogą dotyczyć wierzchołków, a nie całych ścian. Zamień w programie funkcję Tetraedr() na następującą:
//--------------------------------------------------------------------------- void Figure3D(int n, bool cmode, GLfloat v[][3], int s[][3], GLfloat c[][3]) { glBegin(GL_TRIANGLES); for(int i = 0; i < n; i ++) // tworzymy n ścian trójkątnych if(cmode) // tryb koloru { // true - kolor ścian glColor3fv(c[i]); // kolor wg numeru ściany for(int j = 0; j < 3; j++) glVertex3fv(v[ s[i][j] ]); } else // false - kolor wierzchołków for(int j = 0; j < 3; j++) { glColor3fv(c[ s[i][j] ]); // kolor wg numeru wierzchołka glVertex3fv(v[ s[i][j] ]); } glEnd(); } //---------------------------------------------------------------------------
Pierwszy parametr n określa liczbę ścian figury. Drugi parametr określa tryb koloru:
Kod animacji tworzy teraz dwa czworościany, jeden ze stałymi kolorami ścian, a drugi z przejściami tonalnymi. Zastanów się, jak to działa.
// Tutaj umieszczamy program dla OpenGL // Wierzchołki static GLfloat V[][3] = {{ 0.5f,-0.5f, 0.5f}, // v0 {-0.5f, 0.5f, 0.5f}, // v1 { 0.5f, 0.5f,-0.5f}, // v2 {-0.5f,-0.5f,-0.5f}}; // v3 // Ściany static int S[][3] = {{0,1,2}, // S0 {1,3,2}, // S1 {3,0,2}, // S2 {0,3,1}}; // S3 // Kolory ścian static GLfloat C[][3] = {{1.0f,0.0f,0.0f}, // kolor S0, czerwony {0.0f,1.0f,0.0f}, // kolor S1, zielony {0.0f,0.0f,1.0f}, // kolor S2, niebieski {1.0f,1.0f,0.0f}}; // kolor S3, żółty static GLfloat alpha = 0; // kat obrotu glTranslatef(0.0f,0.0f,-5.0f); // cofamy układ 0 5 jednostek glRotatef(alpha,0.0f,1.0f,0.5f); // obracamy układ w płaszczyźnie nieco pochyłej glTranslatef(-1.0f,0.0f,0.0f); // przemieszczamy układ do pierwszej figury for(int i = 0; i < 2; i++) { Figure3D(4,i,V,S,C); // rysujemy figurę glTranslatef(2.0f,0.0f,0.0f); // przemieszczamy układ do drugiej figury } // kąt dla następnej klatki animacji alpha += 1; if(alpha > 360) alpha = 0; // Koniec kodu dla OpenGL
Scenę będą tworzyły dwie piramidy o boku podstawy 1 oraz kwadrat pustyni o boku 3, czyli w sumie trzy obiekty. Kwadrat pustyni ma być poziomy w stosunku do obserwatora, zatem będzie leżał w płaszczyźnie OXZ.
Powyższy rysunek przedstawia ułożenie piramid względem siebie na kwadracie pustyni.
Najpierw musimy określić dane dla pojedynczej piramidy.
Wierzchołki:
v0 = ( 0,0 0,7 0,0)
v1 = ( 0,5 0,0 -0,5)
v2 = ( 0,5 0,0 0,5)
v3 = (-0,5 0,0 0,5)
v4 = (-0,5 0,0 -0,5)
Ściany:
S0 = (v0 v1 v2)
S1 = (v0 v4 v1)
S2 = (v0 v3 v4)
S3 = (v0 v2 v3)
W tych samych tablicach zdefiniujemy kwadrat pustyni. Będzie on zbudowany z dwóch trójkątów:
Wierzchołki:
v5 = ( 1,5 0,0 -1,5)
v6 = ( 1,5 0,0 1,5)
v7 = (-1,5 0,0 1,5)
v8 = (-1,5 0,0 -1,5)
Ściany:
S4 = (v5 v6 v7)
S5 = (v7 v8 v5)
Kod animacji:
// Tutaj umieszczamy program dla OpenGL // Definiowanie figury static GLfloat V[][3] = {{ 0.0f, 0.7f, 0.0f}, // v0 - wierzchołki piramidy { 0.5f, 0.0f,-0.5f}, // v1 { 0.5f, 0.0f, 0.5f}, // v2 {-0.5f, 0.0f, 0.5f}, // v3 {-0.5f, 0.0f,-0.5f}, // v4 { 1.5f, 0.0f,-1.5f}, // v5 - wierzchołki kwadratu pustyni { 1.5f, 0.0f, 1.5f}, // v6 {-1.5f, 0.0f, 1.5f}, // v7 {-1.5f, 0.0f,-1.5f}}; // v8 static int S[][3] = {{0,1,2}, // S0 - ściany piramidy {0,4,1}, // S1 {0,3,4}, // S2 {0,2,3}, // S3 {5,6,7}, // S4 - ściany pustyni {7,8,5}}; // S5 static GLfloat C[][3] = {{1.0f,1.0f,0.0f}, // kolor S0, żółty {0.8f,0.8f,0.0f}, // kolor S1, żółty {0.6f,0.6f,0.0f}, // kolor S2, żółty {0.8f,0.8f,0.0f}, // kolor S3, żółty {0.8f,0.6f,0.2f}, // kolor pustyni {0.8f,0.6f,0.2f}}; // kolor pustyni static GLfloat alpha = 0; // kąt obrotu glTranslatef(0.0f,-0.3f,-4.0f); // cofamy i obniżamy układ glRotatef(15.0f,1.0f,1.0f,0.0f); // pochylamy go nieco w kierunku obserwatora glRotatef(alpha,0.0f,1.0f,0.0f); // obrót o zmienny kąt Figure3D(2,true,V,&S[4],&C[4]); // rysujemy pustynię jako dwa trójkąty glTranslatef(-0.7f,0.0f,-0.7f); // pozycja pierwszej piramidy Figure3D(4,true,V,S,C); // pierwsza piramida glTranslatef(1.4f,0.0f,1.4f); // pozycja drugiej piramidy Figure3D(4,true,V,S,C); // druga piramida // kąt dla następnej klatki animacji alpha += 1; if(alpha > 360) alpha = 0; // Koniec kodu dla OpenGL
Domek będzie definiowało 10 wierzchołków:
Wierzchołki:
v0 = ( 1,0 0,0 -0,5)
v1 = ( 1,0 0,0 0,5)
v2 = ( 1,0 1,0 0,5)
v3 = ( 1,0 1,5 0,0)
v4 = ( 1,0 1,0 -0,5)
v5 = (-1,0 0,0 -0,5)
v6 = (-1,0 0,0 0,5)
v7 = (-1,0 1,0 0,5)
v8 = (-1,0 1,5 0,0)
v9 = (-1,0 1,0 -0,5)
Teraz na podstawie wierzchołków określimy poszczególne ściany, których jest 14. Podział ścian domku na trójkąty jest tutaj dowolny. Wybraliśmy jeden z możliwych.
Ściany:
S0 = (v0 v1 v4)
S1 = (v1 v2 v4)
S2 = (v2 v3 v4)
S3 = (v1 v6 v7)
S4 = (v1 v7 v2)
S5 = (v2 v7 v3)
S6 = (v7 v8 v3)
S7 = (v6 v5 v7)
S8 = (v5 v9 v7)
S9 = (v7 v9 v8)
S10 = (v0 v4 v5)
S11 = (v5 v4 v9)
S12 = (v8 v9 v4)
S13 = (v3 v8 v4)
Mając definicje wierzchołków i ścian, możemy stworzyć prostą aplikację testową:
// Tutaj umieszczamy program dla OpenGL // Definiowanie figury static GLfloat V[][3] = {{ 1.0f, 0.0f,-0.5f}, // v0 { 1.0f, 0.0f, 0.5f}, // v1 { 1.0f, 1.0f, 0.5f}, // v2 { 1.0f, 1.5f, 0.0f}, // v3 { 1.0f, 1.0f,-0.5f}, // v4 {-1.0f, 0.0f,-0.5f}, // v5 {-1.0f, 0.0f, 0.5f}, // v6 {-1.0f, 1.0f, 0.5f}, // v7 {-1.0f, 1.5f, 0.0f}, // v8 {-1.0f, 1.0f,-0.5f}}; // v9 static int S[][3] = {{0,1,4}, // S0 {1,2,4}, // S1 {2,3,4}, // S2 {1,6,7}, // S3 {1,7,2}, // S4 {2,7,3}, // S5 {7,8,3}, // S6 {6,5,7}, // S7 {5,9,7}, // S8 {7,9,8}, // S9 {0,4,5}, // S10 {5,4,9}, // S11 {8,9,4}, // S12 {3,8,4}}; // S13 static GLfloat C[][3] = {{0.6f,0.6f,0.6f}, // Kolor ściany S0 {0.6f,0.6f,0.6f}, // Kolor ściany S1 {0.6f,0.6f,0.6f}, // Kolor ściany S2 {0.7f,0.7f,0.7f}, // Kolor ściany S3 {0.7f,0.7f,0.7f}, // Kolor ściany S4 {0.6f,0.0f,0.0f}, // Kolor ściany S5 {0.6f,0.0f,0.0f}, // Kolor ściany S6 {0.6f,0.6f,0.6f}, // Kolor ściany S7 {0.6f,0.6f,0.6f}, // Kolor ściany S8 {0.6f,0.6f,0.6f}, // Kolor ściany S9 {0.7f,0.7f,0.7f}, // Kolor ściany S10 {0.7f,0.7f,0.7f}, // Kolor ściany S11 {0.5f,0.0f,0.0f}, // Kolor ściany S12 {0.5f,0.0f,0.0f}}; // Kolor ściany S13 static GLfloat alpha = 0; // kąt obrotu glTranslatef(0.0f,-0.3f,-4.0f); // cofamy i obniżamy układ glRotatef(15.0f,1.0f,1.0f,0.0f); // pochylamy go nieco w kierunku obserwatora glRotatef(alpha,0.0f,1.0f,0.0f); // obrót o zmienny kąt Figure3D(14,true,V,S,C); // rysujemy domek // kąt dla następnej klatki animacji alpha += 1; if(alpha > 360) alpha = 0; // Koniec kodu dla OpenGL
Resztę pracy wykonaj sam. Napisz program, który narysuje kwadrat murawy w kolorze ciemnozielonym, a następnie rozmieść na nim domki. Stwórz kilka wariantów takiego rozmieszczenia. Wykorzystaj powyższy kod do animacji widoku.
I Liceum Ogólnokształcące |
Pytania proszę przesyłać na adres email: i-lo@eduinf.waw.pl
W artykułach serwisu są używane cookies. Jeśli nie chcesz ich otrzymywać,
zablokuj je w swojej przeglądarce.
Informacje dodatkowe