Prezentowane materiały są przeznaczone dla uczniów szkół ponadgimnazjalnych. Autor artykułu: mgr Jerzy Wałaszek, wersja1.0 |
©2011 mgr
Jerzy Wałaszek
|
Do wykonania obrotu używamy funkcji:
glRotatef(alfa,x,y,z);
alfa | – | kąt obrotu wokół osi w kierunku przeciwnym od ruchu wskazówek zegara. Kąt jest podany w stopniach. |
x,y,z | – | współrzędne końca wektora, który definiuje oś obrotu. Początek wektora jest w początku układu współrzędnych, a koniec w punkcie (x,y,z). |
Poniższy program rysuje na płaszczyźnie OXY trzy kwadraty, a następnie obraca je kolejno wokół osi równoległych od OX, OY i OZ.
W funkcji obsługi zdarzenia onResize dodaj komentarz przed wywołaniem funkcji glEnable(GL_CULL_FACE), aby nie były usuwane ściany po odwróceniu się tyłem do obserwatora:
//--------------------------------------------------------------------------- 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); glEnable(GL_DEPTH_TEST); 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 } //---------------------------------------------------------------------------
Następnie zmodyfikuj funkcję obsługi zdarzenia onTimer:
// Tutaj umieszczamy program dla OpenGL static GLfloat alpha = 0; // kat obrotu glTranslatef(-1, 0,-4); // przesuwamy się do pierwszego kwadratu for(int i = 0; i < 3; i++) { switch(i) // obracamy układ i ustalamy kolor kwadratu { case 0: glRotatef(alpha,1,0,0); glColor3f(1,1,0); break; // oś OX case 1: glRotatef(alpha,0,1,0); glColor3f(0,1,1); break; // oś OY case 2: glRotatef(alpha,0,0,1); glColor3f(1,0,1); break; // oś OZ } // rysujemy kwadrat odpowiednio obrócony glBegin(GL_QUADS); glVertex2f( 0.4, 0.4); glVertex2f( 0.4,-0.4); glVertex2f(-0.4,-0.4); glVertex2f(-0.4, 0.4); glEnd(); switch(i) // cofamy obrót { case 0: glRotatef(-alpha,1,0,0); break; // oś OX case 1: glRotatef(-alpha,0,1,0); break; // oś OY case 2: glRotatef(-alpha,0,0,1); break; // oś OZ } glTranslatef(1.0f,0.0f,0.0f); // pozycja następnego kwadratu } // kąt dla następnej klatki animacji alpha += 1; if(alpha > 360) alpha = 0; // Koniec kodu dla OpenGL
Program najpierw przesuwa układ współrzędnych do pozycji pierwszego kwadratu. Następnie układ współrzędnych jest obracany wokół osi OX o kąt alpha. W tak obróconym układzie rysujemy pierwszy żółty kwadrat. Cofamy wykonany obrót (później poznamy lepsze sposoby przywracania stanu przekształceń) i przesuwamy układ współrzędnych do pozycji środkowego kwadratu. Wykonujemy obrót wokół osi OY i rysujemy kwadrat jasnoniebieski. Cofamy obrót, przesuwamy się na pozycję trzeciego kwadratu. Tutaj obracamy układ wokół osi OZ, rysujemy kwadrat buraczkowy. Ponieważ kąt alpha zmienia się przy każdej klatce animacji, kwadraty obracają się wokół kolejnych osi symetrii. Spróbuj w programie zmienić kolor ostatniego wierzchołka kwadratu, np. na biały. Dzięki temu łatwiej będzie je identyfikować.
Skalowanie powiększa lub pomniejsza obiekt przez przemnożenie współrzędnych wierzchołków przez odpowiednie współczynniki skali dla każdej z osi układu współrzędnych. Do skalowania wykorzystujemy funkcję:
glScalef(GLfloat x, GLfloat y, GLfloat z);
Poniższy kod rysuje kwadrat odpowiednio wyskalowany wzdłuż osi OX i OY. Współczynniki skali zmieniają się płynnie.
// Tutaj umieszczamy program dla OpenGL static GLfloat Sx = 1; // współczynnik skali wzdłuż osi OX static GLfloat Sy = 1; // współczynnik skali wzdłuż osi OY static GLfloat dSx = 0.02; // przyrost dla Sx static GLfloat dSy = 0.03; // przyrost dla Sy glTranslatef(0.0f,0.0f,-8.0f); // cofamy układ o 8 jednostek glScalef(Sx,Sy,1.0f); // skalujemy układ // modyfikujemy współczynniki skal Sx += dSx; if(Sx < 0.1 || Sx > 3) dSx = -dSx; Sy += dSy; if(Sy < 0.1 || Sy > 3) dSy = -dSy; glBegin(GL_QUADS); // rysujemy kwadrat glVertex2f( 1.0f, 1.0f); glVertex2f( 1.0f,-1.0f); glVertex2f(-1.0f,-1.0f); glVertex2f(-1.0f, 1.0f); glEnd(); // Koniec kodu dla OpenGL
Skalowanie można oczywiście połączyć z innymi transformacjami układu współrzędnych – ważna jest tutaj kolejność wykonywania przekształceń. Poniższy kod dodatkowo obraca kwadrat wokół jego pionowej osi symetrii.
// Tutaj umieszczamy program dla OpenGL static GLfloat Sx = 1; // współczynnik skali wzdłuż osi OX static GLfloat Sy = 1; // współczynnik skali wzdłuż osi OY static GLfloat dSx = 0.02; // przyrost dla Sx static GLfloat dSy = 0.03; // przyrost dla Sy static GLfloat alpha = 0; // kat obrotu glTranslatef(0.0f,0.0f,-8.0f); // cofamy układ o 8 jednostek glRotatef(alpha,0.0f,1.0f,0.0f); // obracamy układ glScalef(Sx,Sy,1.0f); // skalujemy układ // modyfikujemy współczynniki skal Sx += dSx; if(Sx < 0.1 || Sx > 3) dSx = -dSx; Sy += dSy; if(Sy < 0.1 || Sy > 3) dSy = -dSy; alpha += 1; if(alpha >= 360) alpha = 0; glBegin(GL_QUADS); // rysujemy kwadrat glVertex2f( 1.0f, 1.0f); glVertex2f( 1.0f,-1.0f); glVertex2f(-1.0f,-1.0f); glVertex2f(-1.0f, 1.0f); glEnd(); // Koniec kodu dla OpenGL
Nazwa stałej | Opis |
GL_POINTS |
Punkty. Każde glVertex##() definiuje jeden punkt. |
GL_LINES |
Linie. Każde dwa kolejne glVertex##() definiują jedną linię. |
GL_LINE_STRIP |
Łamana otwarta. Kolejne glVertex##() definiują kolejne wierzchołki łamanej. |
GL_LINE_LOOP |
Łamana zamknięta. Kolejne glVertex##() definiują kolejne wierzchołki łamanej. Ostatni wierzchołek jest łączony linią z pierwszym. |
GL_TRIANGLES |
Trójkąt. Każde trzy kolejne glVertex##() definiują wierzchołki trójkąta. |
GL_QUADS |
Czworokąt. Każde cztery kolejne glVertex##() definiują wierzchołki czworokąta. |
Kolejnym prymitywem jest wielokąt, który tworzymy w bloku glBegin(GL_POLYGON);... glEnd(); Wewnątrz bloku określamy kolejne wierzchołki wielokąta.
Dołącz do programu plik nagłówkowy math.h i przepisz poniższy kod do funkcji obsługi zdarzenia onTimer.
// Tutaj umieszczamy program dla OpenGL static GLfloat alpha = 0; // kąt obrotu glTranslatef(0.0f,0.0f,-3.0f); // cofamy układ o 3 jednostki glRotatef(alpha,1.0f,1.0f,1.0f); // obracamy układ // modyfikujemy kąt obrotu alpha += 1; if(alpha >= 360) alpha = 0; glBegin(GL_POLYGON); // rysujemy wielobok for(int i = 0; i < 8; i++) { switch(i) // dla każdego wierzchołka określamy inny kolor { case 0: glColor3f(1.0f,0.0f,0.0f); break; case 1: glColor3f(0.0f,1.0f,0.0f); break; case 2: glColor3f(0.0f,0.0f,1.0f); break; case 3: glColor3f(1.0f,1.0f,0.0f); break; case 4: glColor3f(0.0f,1.0f,1.0f); break; case 5: glColor3f(1.0f,0.0f,1.0f); break; case 6: glColor3f(1.0f,1.0f,1.0f); break; case 7: glColor3f(0.5f,0.0f,0.0f); break; } glVertex2f(cos(6.28*i/8),sin(6.28*i/8)); } glEnd(); // Koniec kodu dla OpenGL
Jeśli po uruchomieniu wielokąt znika po wykonaniu połowy obrotu, to znaczy, że masz włączone ukrywanie odwróconych ścian. W funkcji obsługi zdarzenia onResize zakomentuj trzy ostatnie wywołania funkcji, co spowoduje wyłączenie ukrywania odwróconych ścian i wielokąt będzie cały czas widoczny. Opcję tę wyjaśnimy dokładnie później.
Jeśli dokładnie przyjrzysz się wielokątowi, to zauważysz, iż faktycznie składa się on z trójkątów, których wspólnym wierzchołkiem jest punkt startowy (wierzchołek ten ma kolor czerwony).
Kolejnym prymitywem OpenGL jest wstęga trójkątów, którą tworzy się w bloku glBegin(GL_TRANGLE_STRIP); ... glEnd();. Wewnątrz bloku podajemy współrzędne trzech punktów dla początkowego trójkąta, a każdy następny wierzchołek definiuje przylegający trójkąt. Spójrz na poniższe rysunki objaśniające:
Najpierw tworzymy trójkąt podstawowy z wierzchołków v0, v1 i v2:
Kolejny wierzchołek v3 tworzy trójkąt z dwoma ostatnimi wierzchołkami: v1 i v2:
Następny wierzchołek v4 znów tworzy trójkąt z dwoma ostatnimi wierzchołkami: v2 i v3:
Każdy kolejny wierzchołek utworzy następny trójkąt z dwoma poprzednimi wierzchołkami.
Zaletą wstęgi trójkątów jest mniejsza liczba wywołań funkcji glVertex##() w porównaniu z rysowaniem osobnych trójkątów. Przekłada się to na szybkość, jeśli liczba trójkątów jest bardzo duża.
Poniższy program obraca obręcz zdefiniowaną jako zamknięta wstęga trójkątów w przestrzeni 3D. Obręcz jest tworzona z punktów okręgów leżących na dwóch płaszczyznach: OXY oraz na płaszczyźnie równoległej przesuniętej o 0.5 jednostki.
// Tutaj umieszczamy program dla OpenGL // glPolygonMode(GL_FRONT_AND_BACK,GL_LINE); static GLfloat alpha = 0; // kat obrotu glTranslatef(0.0f,0.0f,-3.0f); // cofamy układ o 3 jednostki glRotatef(alpha,1.0f,1.0f,1.0f); // obracamy układ // modyfikujemy kąt obrotu alpha += 1; if(alpha >= 360) alpha = 0; glBegin(GL_TRIANGLE_STRIP); // wielobok for(int i = 0; i < 36; i++) { glColor3f(1-sin(3.14*i/35.0), 0.25*sin(3.14*i/35.0), 0.25); glVertex2f(cos(6.2830*i/35.0), sin(6.2830*i/35.0)); glVertex3f(cos(6.2830*i/35.0), sin(6.2830*i/35.0), 0.5f); } glEnd(); // Koniec kodu dla OpenGL
Jeśli powiększysz okno i dokładnie przyjrzysz się wstędze, to zauważysz krawędź łączenia. Jest ona czasami widoczna, ponieważ punkty generujemy za pomocą funkcji trygonometrycznych i punkty końcowe nie pokrywają się dokładnie z punktami startowymi. Gdyby tak było, połączenia nie moglibyśmy zobaczyć. Możesz poeksperymentować ze zwiększeniem dokładności stałej 2π w kodzie programu lub inaczej zorganizować pętlę.
Na początku kodu jest zakomentowana funkcja:
// glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
Jeśli usuniesz komentarz, to wywołanie tej funkcji spowoduje rysowanie jedynie linii trójkątów bez ich wypełnienia. Tryb ten można stosować do testowania tworzonej sceny.
Wypróbuj poniższy kod:
// Tutaj umieszczamy program dla OpenGL // glPolygonMode(GL_FRONT_AND_BACK,GL_LINE); static GLfloat alpha = 0; // kat obrotu glTranslatef(0.0f,0.0f,-3.0f); // cofamy układ o 3 jednostki glRotatef(alpha,1.0f,1.0f,1.0f); // obracamy układ // modyfikujemy kąt obrotu alpha += 1; if(alpha >= 360) alpha = 0; glBegin(GL_TRIANGLE_STRIP); for(int i = 0; i < 36; i++) { glColor3f(1-sin(3.14*i/35.0), 0.25*sin(3.14*i/35.0), 0.25); glVertex3f(cos(6.2830*i/35.0), sin(6.2830*i/35.0),-0.25f); glVertex3f(cos(6.2830*i/35.0), sin(6.2830*i/35.0), 0.25f); } glEnd(); glScalef(0.95f,0.95f,0.95f); glRotatef(alpha,0.0f,1.0f,0.0f); glBegin(GL_TRIANGLE_STRIP); for(int i = 0; i < 36; i++) { glColor3f(sin(3.14*i/35.0), 1-sin(3.14*i/35.0), 0.25); glVertex3f(cos(6.2830*i/35.0), sin(6.2830*i/35.0),-0.25f); glVertex3f(cos(6.2830*i/35.0), sin(6.2830*i/35.0), 0.25f); } glEnd(); // Koniec kodu dla OpenGL
Czy potrafisz wyjaśnić zasadę jego działania?
Następnym prymitywem OpenGL jest wachlarz trójkątów, który tworzymy wewnątrz bloku glBegin(GL_TRIANGLE_FAN); ... glEnd();. Zasada tworzenia wachlarza trójkątów jest następująca:
Pierwsze trzy wierzchołki v0, v1 i v2 definiują pierwszy trójkąt.
Kolejny wierzchołek v3 tworzy nowy trójkąt z wierzchołkiem v0 oraz v2, który go bezpośrednio poprzedza.
Następny wierzchołek v4 tworzy kolejny trójkąt z v0 oraz v3, który bezpośrednio go poprzedza.
Zatem każdy nowy wierzchołek vi definiuje nowy trójkąt, którego pozostałe dwa wierzchołki to v0 oraz vi-1. Poniższy kod tworzy stożek. Składa się z dwóch wachlarzy trójkątów. Jeden wachlarz jest powierzchnią boczną stożka, a drugi tworzy podstawę.
// Tutaj umieszczamy program dla OpenGL // glPolygonMode(GL_FRONT_AND_BACK,GL_LINE); static GLfloat alpha = 0; // kat obrotu glTranslatef(0.0f,0.0f,-4.0f); // cofamy układ o 4 jednostki glRotatef(alpha,1.0f,1.0f,1.0f); // obracamy układ // modyfikujemy kąt obrotu alpha += 1; if(alpha >= 360) alpha = 0; glBegin(GL_TRIANGLE_FAN); // podstawa stożka glColor3f(1.0f,1.0f,0.0f); glVertex2f(0.0f, 0.0f); // środek for(int i = 0; i < 36; i++) // punkty na obrzeżu { glColor3f(0, 1-sin(3.14*i/35.0), 1.0f); glVertex2f(cos(6.2830*i/35.0), sin(6.2830*i/35.0)); } glEnd(); glBegin(GL_TRIANGLE_FAN); // ściana boczna stożka glColor3f(1.0f,0.0f,0.0f); glVertex3f(0.0f, 0.0f, 1.5f); // wierzchołek for(int i = 35; i >= 0; i--) // punkty na obrzeżu { glColor3f(1-sin(3.14*i/35.0), 1.0f, 0.0f); glVertex2f(cos(6.2830*i/35.0), sin(6.2830*i/35.0)); } glEnd(); // Koniec kodu dla OpenGL
Jeśli w obsłudze zdarzenia onResize blokowałeś trzy ostatnie funkcje, to je odblokuj, usuwając sprzed nich komentarze. Następnie odblokuj funkcję:
// glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
Po uruchomieniu programu otrzymasz stożek, w którym trójkąty są rysowane liniami i nie posiadają wypełnienia. Odblokowane w onResize funkcje ukrywają ściany, które są odwrócone tyłem do obserwatora – ma to sens, ponieważ w figurach wypukłych takie ściany i tak nie są widoczne, a ich rysowanie niepotrzebnie zajmuje czas komputerowi. Więcej o zasadach ukrywania ścian napiszemy później.
Teraz ponownie wyłącz ukrywanie ścian, zaopatrz się w okulary anaglifowe (lewe szkoło czerwone, prawe niebieskozielone), wpisz poniższy program, uruchom go i ciesz się efektem 3D (prezentowane tutaj rozwiązanie nie jest najlepsze, ponieważ linie czerwone i zielononiebieskie się nawzajem przysłaniają)..
// Tutaj umieszczamy program dla OpenGL glPolygonMode(GL_FRONT_AND_BACK,GL_LINE); static GLfloat alpha = 0; // kat obrotu for(int j = 0; j < 2; j++) { glLoadIdentity(); glTranslatef(-0.3 + 0.6*j,0.0f,-5.0f); glRotatef(alpha,1.0f,1.0f,1.0f); glColor3f(j,(1-j)/2.0,(1-j)/2.0); glBegin(GL_TRIANGLE_FAN); // podstawa stożka glVertex2f(0.0f, 0.0f); // środek for(int i = 0; i < 36; i++) // punkty na obrzeżu glVertex2f(cos(6.2830*i/35.0), sin(6.2830*i/35.0)); glEnd(); glBegin(GL_TRIANGLE_FAN); // ściana boczna stożka glVertex3f(0.0f, 0.0f, 1.5f); // wierzchołek for(int i = 35; i >= 0; i--) // punkty na obrzeżu glVertex2f(cos(6.2830*i/35.0), sin(6.2830*i/35.0)); glEnd(); } // modyfikujemy kąt obrotu alpha += 1; if(alpha >= 360) alpha = 0; // Koniec kodu dla OpenGL
Włącz ukrywanie ścian i ponownie uruchom program. Może dodasz ruch w przestrzeni 3D?
Wstęgę czworokątów tworzymy w bloku glBegin(GL_QUAD_STRIP); ... glEnd();. Wierzchołki podajemy w następującej kolejności:
Pierwsze dwa wierzchołki v0 i v1 definiują krawędź czworokąta:
Kolejne dwa wierzchołki v2 i v3 definiują krawędź naprzeciwległą. Zwróć uwagę, że podawane są one w tej samej kolejności co v0 i v1:
Następne dwa wierzchołki v4 i v5 zdefiniują krawędź nowego czworokąta, który ma wspólne dwa poprzednie wierzchołki z poprzedzającym go czworokątem na wstędze:
Każde kolejne dwa wierzchołki dodają do wstęgi nowy czworokąt:
Poniższy kod tworzy walec. Wykorzystuje on dwa wachlarze trójkątów na podstawę i górę walca oraz wstęgę czworokątów na powierzchnię boczną.
// Tutaj umieszczamy program dla OpenGL //glPolygonMode(GL_FRONT_AND_BACK,GL_LINE); static GLfloat alpha = 0; // kat obrotu GLfloat s,c; // sinus i cosinus alpha += 1; if(alpha >= 360) alpha = 0; glTranslatef(0.0f,0.0f,-4.0f); glRotatef(alpha,1.0f,0.5f,0.3f); glBegin(GL_QUAD_STRIP); // powierzchnia boczna for(int i = 0; i < 37; i++) { if(i == 36) { s = 0.0f; c = 1.0f; } else { s = sin(6.2830 * i/36.0); c = cos(6.2830 * i/36.0); } glColor3f(s,0.5f,1.0f-s); glVertex3f(c,s,-1.0f); glVertex3f(c,s, 1.0f); } glEnd(); glTranslatef(0.0f,0.0f,-1.0f); glColor3f(1.0f,0.0f,0.0f); // kolor czerwony glBegin(GL_TRIANGLE_FAN); // podstawa glVertex2f(0.0f,0.0f); for(int i = 36; i >= 0; i--) { if(i == 36) { s = 0.0f; c = 1.0f; } else { s = sin(6.2830 * i/36.0); c = cos(6.2830 * i/36.0); } glVertex2f(s,c); } glEnd(); glTranslatef(0.0f, 0.0f, 2.0f); glColor3f(1.0f,1.0f,0.0f); // kolor żółty glBegin(GL_TRIANGLE_FAN); // podstawa glVertex2f(0.0f,0.0f); for(int i = 0; i < 37; i++) { if(i == 36) { s = 0.0f; c = 1.0f; } else { s = sin(6.2830 * i/36.0); c = cos(6.2830 * i/36.0); } glVertex2f(s,c); } glEnd(); // Koniec kodu dla OpenGL
Poeksperymentuj z włączaniem i wyłączaniem ścian oraz z ukrywaniem ścian odwróconych tyłem.
glTranslatef(dx,dy,dz) | – | przesunięcie układu współrzędnych wzdłuż kolejnych osi. |
glRotatef(kąt,x,y,z) | – | obrót układu współrzędnych o dany kąt wokół osi przechodzącej przez środek układu oraz punkt (x,y,z). |
glScalef(Sx,Sy,Sz) | – | skalowanie układu współrzędnych wzdłuż kolejnych osi wg podanych współczynników skali. |
Prymitywy OpenGL definiowane przez wierzchołki w bloku glBegin(); ... glEnd();
Prymityw | Opis |
GL_POINTS |
Punkty. Każde glVertex##() definiuje jeden punkt. |
GL_LINES |
Linie. Każde dwa kolejne glVertex##() definiują jedną linię. |
GL_LINE_STRIP |
Łamana otwarta. Kolejne glVertex##() definiują kolejne wierzchołki łamanej. |
GL_LINE_LOOP |
Łamana zamknięta. Kolejne glVertex##() definiują kolejne wierzchołki łamanej. Ostatni wierzchołek jest łączony linią z pierwszym. |
GL_TRIANGLES |
Trójkąt. Każde trzy kolejne glVertex##() definiują wierzchołki trójkąta. |
GL_QUADS |
Czworokąt. Każde cztery kolejne glVertex##() definiują wierzchołki czworokąta. |
GL_POLYGON |
Wielokąt. |
GL_TRIANGLE_STRIP |
Wstęga trójkątów. Pierwsze trzy wierzchołki definiują początkowy trójkąt. Każdy następny wierzchołek definiuje trójkąt zbudowany z tego wierzchołka oraz dwóch poprzednich. |
GL_TRIANGLE_FAN |
Wachlarz trójkątów. Pierwsze trzy wierzchołki definiują początkowy trójkąt. Każdy następny wierzchołek dodaje trójkąt zbudowany z tego wierzchołka, pierwszego wierzchołka oraz poprzedniego wierzchołka. |
GL_QUAD_STRIP |
Wstęga czworokątów. Dwa początkowe wierzchołki definiują bok startowy. Każde następne dwa definiują boki naprzeciwległo kolejnych czworokątów. |
Przedstawione tu prymitywy pozwalają tworzyć dowolne obiekty 3D.
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