Prezentowane materiały są przeznaczone dla uczniów szkół ponadgimnazjalnych. Autor artykułu: mgr Jerzy Wałaszek, wersja1.0 |
©2011 mgr
Jerzy Wałaszek
|
Biblioteka OpenGL (Open Graphic Library - Otwarta Biblioteka Graficzna) zawiera funkcje, które umożliwiają tworzenie zaawansowanej grafiki trójwymiarowej. Programy napisane pod tę bibliotekę można szybko przenosić na inne platformy, np. do systemu Linux.
Zanim zaczniemy tworzyć jakąkolwiek grafikę 3D, musimy utworzyć odpowiedni projekt w środowisku Borland C++ Builder, który posłuży za szablon przyszłych projektów. W tym celu utwórz projekt, który zawiera pojedynczą formę i umieść na niej komponent Timer (z palety System). Następnie ustaw własności wg poniższej listy:
Form1
Caption = Grafika OpenGL
ClientHeight = 512
ClientWidth = 512
Name = frm3D
Position = poScreenCenter
Timer1
Name = tmrAnimate
Zapisz projekt na dysku. Nadaj mu nazwę glprj. Moduł zapisz pod nazwą glunit.
Do katalogu projektowego skopiuj poniższe dwa pliki:
Dodaj do projektu plik glaux.lib. W tym celu wybierz z menu opcje Project→Add to Project.., w oknie dialogowym wybierz katalog projektowy, następnie na dole wybierz pliki typu Library files lib., wskaż plik glaux.lib i zatwierdź go przyciskiem Otwórz.
Uwaga
Teraz przystąpimy do tworzenia kodu. Wciśnij klawisz F12, aby przejść do edytora. Następnie naciśnij Ctrl+F6, aby do edytora załadować plik nagłówkowy modułu glunit.h. Pod dyrektywami #include dopisz:
#ifndef glunitH #define glunitH #include <Classes.hpp> #include <Controls.hpp> #include <StdCtrls.hpp> #include <Forms.hpp> #include <ExtCtrls.hpp> #include <gl/gl.h> #include <gl/glu.h> #include <gl/glaux.h> #include <float.h> //--------------------------------------------------------------------------- class Tfrm3D : public TForm { __published: // IDE-managed Components
Teraz w klasie obiektu dodajemy kilka definicji w sekcjach private i public:
private: // User declarations HDC hdc; HGLRC hrc; int PixelFormat; public: // User declarations __fastcall Tfrm3D(TComponent* Owner); void __fastcall SetPixelFormatDescriptor(); void __fastcall SetupRC(); }; //--------------------------------------------------------------------------- extern PACKAGE Tfrm3D *frm3D; //--------------------------------------------------------------------------- #endif
Naciśnij ponownie Ctrl+F6, aby w oknie edytora pojawił się kod modułu glunit.cpp. W konstruktorze klasy umieść kod:
//--------------------------------------------------------------------------- __fastcall Tfrm3D::Tfrm3D(TComponent* Owner) : TForm(Owner) { _control87(MCW_EM, MCW_EM); } //---------------------------------------------------------------------------
Teraz musimy wpisać do modułu kilka funkcji pomocniczych - nie przejmuj się, jeśli nie rozumiesz ich przeznaczenia w tym momencie. Są one potrzebne do ustawienia parametrów biblioteki OpenGL w środowisku Borland C++ Builder. Funkcje umieść na końcu modułu.
//--------------------------------------------------------------------------- void __fastcall Tfrm3D::SetPixelFormatDescriptor() { PIXELFORMATDESCRIPTOR pfd = { sizeof(PIXELFORMATDESCRIPTOR), 1, PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, PFD_TYPE_RGBA, 24, 0,0,0,0,0,0, 0,0, 0,0,0,0,0, 32, 0, 0, PFD_MAIN_PLANE, 0, 0,0,0 }; PixelFormat = ChoosePixelFormat(hdc, &pfd); SetPixelFormat(hdc, PixelFormat, &pfd); } //--------------------------------------------------------------------------- void __fastcall Tfrm3D::SetupRC() { glClearColor(0,0,0,0); glClear(GL_COLOR_BUFFER_BIT); glFlush(); } //---------------------------------------------------------------------------
Zadaniem funkcji SetPixelFormatDescriptor jest przygotowanie Windows do rysowania za pomocą biblioteki OpenGL na obiekcie Canvas okna aplikacji.
Funkcja SetupRC ustawia kolor tła okna i wypełnia tym kolorem bufor koloru biblioteki OpenGL.
Reszta niezbędnych funkcji to funkcje obsługi zdarzeń formy oraz komponentu Timer. Przypominam, że funkcję obsługi zdarzenia tworzy się w okienku Object Inspector na zakładce Events przez dwukrotne kliknięcie myszką po prawej stronie nazwy zdarzenia. Wtedy środowisko tworzy szablon odpowiedniej funkcji. Należy jedynie wypełnić jej treść.
Utwórz funkcję obsługi zdarzenia OnCreate dla formy i wpisz poniższy kod:
//--------------------------------------------------------------------------- void __fastcall Tfrm3D::FormCreate(TObject *Sender) { hdc = GetDC(Handle); SetPixelFormatDescriptor(); hrc = wglCreateContext(hdc); wglMakeCurrent(hdc, hrc); SetupRC(); } //---------------------------------------------------------------------------
Utwórz funkcję obsługi zdarzenia OnDestroy i wpisz poniższy kod:
//--------------------------------------------------------------------------- void __fastcall Tfrm3D::FormDestroy(TObject *Sender) { ReleaseDC(0,hdc); wglMakeCurrent(hdc, NULL); wglDeleteContext(hrc); } //---------------------------------------------------------------------------
Nasz szablon będzie obsługiwał zmianę rozmiaru okna. Dlatego utwórz funkcję obsługi zdarzenia onResize i wprowadź do niej poniższy 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); 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 } //---------------------------------------------------------------------------
Ostatnie zdarzenie odnosi się do komponentu tmrAnimate (Timer). Kliknij jego nazwę w oknie Object TreeView. Następnie utwórz funkcję obsługi zdarzenia onTimer i wpisz do niej poniższy kod:
//--------------------------------------------------------------------------- void __fastcall Tfrm3D::tmrAnimateTimer(TObject *Sender) { tmrAnimate->Enabled = false; // blokujemy timer glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity(); // zerujemy przekształcenia // Tutaj umieszczamy program dla OpenGL glTranslatef(0.0f,0.0f,-6.0f); glBegin(GL_TRIANGLES); // rysujemy trójkąt glVertex2f( 0.0f, 1.0f); glVertex2f( 1.0f,-1.0f); glVertex2f(-1.0f,-1.0f); glEnd(); // Koniec kodu dla OpenGL SwapBuffers(hdc); tmrAnimate->Enabled = true; // odblokowujemy timer } //---------------------------------------------------------------------------
Wyjaśnienia wymaga blokowanie Timera. Otóż może się zdarzyć, że na wolniejszym komputerze obsługa zdarzenia onTimer będzie dłuższa niż 20 ms. Wtedy następne przerwanie wystąpiłoby w trakcie tworzenia sceny i zniszczyłoby efekty całej naszej pracy. Aby temu zapobiec, funkcja obsługująca zdarzenie onTimer blokuje Timer na samym początku swego działania i odblokowuje go tuż przed zakończeniem.
Kod dla biblioteki OpenGL będziemy umieszczali w podanym miejscu wewnątrz funkcji obsługi zdarzenia onTimer. Przykładowy kod, który teraz tam umieściliśmy, rysuje biały trójkąt. Dokładniej omówimy to w dalszej części zajęć.
Sprawdź projekt, kompilując go i uruchamiając. Jeśli wszystko jest w porządku, powinieneś otrzymać czarne okno z białym trójkątem. Przy zmianie rozmiaru okna trójkąt też odpowiednio zmienia wymiary.
Teraz na podstawie tego projektu utworzymy szablon, który posłuży nam za punkt startowy dla przyszłych projektów. Z menu wybierz opcję Project→Add to Repository. W oknie dialogowym dodawania do składu wpisz:
Pamiętaj, aby nie usuwać z dysku katalogu z tym projektem, ponieważ zawiera on szablon. Aby przetestować ten szablon, utwórz na dysku nowy katalog projektowy, zamknij wszystkie projekty (opcja menu File→Close All), następnie wybierz File→New→Other..., w oknie dialogowym New Items wybierz zakładkę Projects.
Następnie zaznacz Projekt OpenGL i kliknij OK. W oknie dialogowym Select Directory wejdź do katalogu (Uwaga: nie wystarczy go wskazać, musi być otwarty), w którym ma zostać utworzony projekt i kliknij OK. Na podstawie naszego szablonu zostanie utworzona kopia projektu, w której znajdą się już wszystkie pliki. Jesteś gotowy do rozpoczęcia nauki programowania biblioteki OpenGL.
Układ współrzędnych w przestrzeni trójwymiarowej posiada trzy wzajemnie prostopadłe do siebie osie: OX, OY i OZ. Każdy wierzchołek v (ang. vertex) posiada w tym układzie trzy współrzędne vx, vy i vz, które jednoznacznie określają jego położenie w przestrzeni trójwymiarowej.
Jednostka odległości, wg której określane są te współrzędne, jest umowną liczbą, która zależy od ustawień początkowych w OpenGL. Ustawienia widoku w naszej aplikacji dokonywane są wewnątrz obsługi zdarzenia onResize. Znajduje się tam wywołanie funkcji:
gluPerspective(45.0f,(GLfloat)ClientWidth/(GLfloat)ClientHeight,0.1f,300.0f);
Ustawia ona "sposób patrzenia" na tworzoną scenę. Pierwszy parametr, 45.0f, tzw. określa kąt w stopniach ostrosłupa widzenia. Większy kąt obejmie więcej przestrzeni, lecz spowoduje efekt rybiego oka, czyli zniekształcenia perspektywiczne. Możesz sobie później poeksperymentować z tymi parametrami.
Kolejny parametr określa współczynnik kształtu prostokąta widzenia. Jest to stosunek szerokości do wysokości obszaru płótna Canvas. Zwróć uwagę, że przed podzieleniem oba parametry zostają przekonwertowane na postać GLfloat, czyli na liczby zmiennoprzecinkowe typu float. Bez tej konwersji komputer wykonałby dzielenie całkowite, a wynikiem byłoby 1 lub zero, w zależności co większe, wysokość czy szerokość okna.
Ostatnie dwa parametry, 0.1f i 300.0f, to tzw. głębia bufora wyświetlania. Punkty będą rysowane, jeśli ich współrzędne z będą się zawierały w zakresie od -0.1 do -300. Punkty bliższe i dalsze nie będą rysowane.
Współrzędne w OpenGL są liczbami zmiennoprzecinkowymi. Ponieważ oś OZ jest skierowana w kierunku obserwatora, to wierzchołki o współrzędnej z większej są bliżej obserwatora od wierzchołków o współrzędnej z mniejszej.
Układ współrzędnych można przesuwać względem położenia obserwatora przy pomocy funkcji
glTranslatef(dx,dy,dz);
Funkcja ta przyjmuje trzy parametry typu float, które definiują przesunięcie odpowiednio względem osi bieżącego układu. Poeksperymentuj z parametrami tej funkcji w programie.
glBegin(typ) |
– | rozpoczęcie rysowania. Parametr typ określa rodzaj rysowanego obiektu. |
glEnd() |
– | zakończenie rysowania |
Obiekty zawsze rysowane są w bieżącym układzie współrzędnych – jeśli ten układ przesuniesz za pomocą glTranslatef() (lub innych, które poznamy później), to rysowany obiekt będzie przesunięty. Przesunięcie układu współrzędnych nie wpływa na wcześniej narysowane punkty.
Do rysowania punktów wykorzystujemy funkcję ogólną glVertex##(). Literkami ## oznaczyliśmy rodzaj funkcji. OpenGL umożliwia definiowanie współrzędnych na wiele różnych sposobów. Aby nie powodować na początku zbytniego zamieszania, umówmy się, że będziemy korzystali z dwóch rodzajów funkcji:
glVertex2f(x,y) |
– | określa współrzędne punktu na płaszczyźnie OXY, Dla takiego wierzchołka współrzędna z jest równa 0. |
glVertex3f(x,y,z) |
– | określa współrzędne punktu w przestrzeni OXYZ. |
Napiszemy teraz prosty kod, który narysuje na płaszczyźnie OXY cztery punkty w narożnikach kwadratu o boku 2 ze środkiem w środku układu współrzędnych. Kod wprowadzamy do funkcji obsługi zdarzenia on|Timer dla tmrAnimarte w podanym miejscu, o ile nie zostanie wyraźnie napisane inaczej.
// Tutaj umieszczamy program dla OpenGL glTranslatef(0.0f,0.0f,-3.0f); // przesuwamy układ w tył o 3 jednostki glBegin(GL_POINTS); // rysujemy punkty w narożnikach kwadratu glVertex2f( 1.0f, 1.0f); // v1 glVertex2f( 1.0f,-1.0f); // v2 glVertex2f(-1.0f,-1.0f); // v3 glVertex2f(-1.0f, 1.0f); // v4 glEnd(); // Koniec kodu dla OpenGL
Punkty są malutkie. Możemy je powiększyć za pomocą funkcji:
void glPointSize(GLfloat size);
Parametr size określa wielkość punktu. Nie wszystkie wielkości są dostępne w OpenGL. Dla środowiska Windows można przyjąć zakres od 0.5 do 10.0 z krokiem co 0.125. Wywołanie funkcji wstaw przed glBegin() dla wszystkich rysowanych punktów.
// Tutaj umieszczamy program dla OpenGL glTranslatef(0.0f,0.0f,-6.0f); // przesuwamy układ w tył o 3 jednostki glPointSize(10.0f); // duże punkty glBegin(GL_POINTS); // rysujemy punkty w narożnikach kwadratu glVertex2f( 1.0f, 1.0f); // v1 o wielkości 10 glVertex2f( 1.0f,-1.0f); // v2 o wielkości 10 glVertex2f(-1.0f,-1.0f); // v3 o wielkości 10 glVertex2f(-1.0f, 1.0f); // v4 o wielkości 10 glEnd(); // Koniec kodu dla OpenGL
Punkty są zawsze rysowane w takiej wielkości, jaka została ustawiona przed wywołaniem glBegin(). Jeśli zatem chcesz otrzymać punkty o różnych wielkościach, to musisz je rysować w osobnych blokach glBegin()...glEnd().
// Tutaj umieszczamy program dla OpenGL glTranslatef(0.0f,0.0f,-3.0f); // przesuwamy układ w tył o 3 jednostki glPointSize(10.0f); glBegin(GL_POINTS); glVertex2f( 1.0f, 1.0f); // v1 o wielkości 10 glEnd(); glPointSize(5.0f); glBegin(GL_POINTS); glVertex2f( 1.0f,-1.0f); // v2 o wielkości 5 glEnd(); glPointSize(2.0f); glBegin(GL_POINTS); glVertex2f(-1.0f,-1.0f); // v3 o wielkości 2 glEnd(); glPointSize(1.0f); glBegin(GL_POINTS); glVertex2f(-1.0f, 1.0f); // v4 o wielkości 1 glEnd(); // Koniec kodu dla OpenGL
Kolejny przykład będzie rysował punkty algorytmicznie, tak aby wypełniały one sześcian. Dodamy również ruch przez określenie przesunięcia w osi OX i OY układu współrzędnych przed rozpoczęciem rysowania punktów. Przesunięcie to będzie się zmieniało przy każdym przerwaniu onTimer, co w efekcie da wrażenie ruchu figury. Wprowadź kod dla OpenGL:
// Tutaj umieszczamy program dla OpenGL static GLfloat Tx = -2; // przesunięcie w osi OX static GLfloat Ty = -2; // przesunięcie w osi OY static GLfloat dTx = 0.02; // przyrost w osi OX static GLfloat dTy = 0.03; // przyrost w osi OY GLfloat x,y,z; glTranslatef(Tx,Ty,-6.0f); // przesuwamy układ współrzędnych // modyfikujemy przesunięcie dla następnego kadru animacji Tx += dTx; if(Tx < -2 || Tx > 2) dTx = -dTx; Ty += dTy; if(Ty < -2 || Ty > 2) dTy = -dTy; glPointSize(2.0f); // wszystkie punkty o rozmiarze 2 glBegin(GL_POINTS); for(x = -1; x <= 1; x += 0.25) for(y = -1; y <= 1; y += 0.25) for(z = -1; z <= 1; z += 0.25) glVertex3f(x,y,z); glEnd(); // Koniec kodu dla OpenGL
Do określania kolorów punktów służy funkcja
glColor##()
Podobnie jak przy glVertex##(), tutaj również mamy wiele możliwości określenia kolorów. Na początek określmy tę funkcję jako:
glColor3f(r,g,b); | – | Kolor jest definiowany przez trzy składowe,
które mogą przyjmować wartość od 0 do 1: r –
składowa czerwona (ang. Red) |
Funkcję glColor##() możesz umieszczać zarówno przed glBegin() jak i przed każdym wierzchołkiem. Poniższy kod rysuje nasz sześcian z różnymi kolorami punktów, które zależą od położenia punktu w przestrzeni. Poeksperymentuj z nim trochę.
// Tutaj umieszczamy program dla OpenGL static GLfloat Tx = -2; // przesunięcie w osi OX static GLfloat Ty = -2; // przesunięcie w osi OY static GLfloat dTx = 0.02; // przyrost w osi OX static GLfloat dTy = 0.03; // przyrost w osi OY GLfloat x,y,z; glTranslatef(Tx,Ty,-6.0f); // przesuwamy układ w tył o 6 jednostek Tx += dTx; if(Tx < -2 || Tx > 2) dTx = -dTx; Ty += dTy; if(Ty < -2 || Ty > 2) dTy = -dTy; glPointSize(3.0f); // wszystkie punkty o rozmiarze 3 glBegin(GL_POINTS); for(x = -1; x <= 1; x += 0.25) for(y = -1; y <= 1; y += 0.25) for(z = -1; z <= 1; z += 0.25) { glColor3f((x+1)/2,(y+1)/2,1-(y+1)/2); glVertex3f(x,y,z); } glEnd(); // Koniec kodu dla OpenGL
glBegin(GL_LINES); glVertex##(...); // punkt startowy linii glVertex##(...); // punkt końcowy linii ... glEnd();
Poniższy przykład rysuje trzy pary skrzyżowanych linii. Wprowadź go w miejsce kodu rysującego punkty w sześcianie. Zwróć uwagę na sposób rysowania każdej z par – rysujemy jedną, przesuwamy układ współrzędnych i rysujemy drugą.
// Tutaj umieszczamy program dla OpenGL static GLfloat Tx = -2; // przesunięcie w osi OX static GLfloat Ty = -2; // przesunięcie w osi OY static GLfloat dTx = 0.02; // przyrost w osi OX static GLfloat dTy = 0.03; // przyrost w osi OY int i; glTranslatef(Tx,Ty,-6.0f); // przesuwamy układ w tył o 6 jednostek Tx += dTx; if(Tx < -2 || Tx > 2) dTx = -dTx; Ty += dTy; if(Ty < -2 || Ty > 2) dTy = -dTy; for(i = 0; i < 3; i++) { glTranslatef(0.0f,0.0f,-1.0f); // przesuwamy układ o 1 w tył glBegin(GL_LINES); glVertex2f(-1.0f,-1.0f); // początek pierwszej linii glVertex2f( 1.0f, 1.0f); // koniec pierwszej linii glVertex2f(-1.0f, 1.0f); // początek drugiej linii glVertex2f( 1.0f,-1.0f); // koniec drugiej linii glEnd(); } // Koniec kodu dla OpenGL
Kolory linii ustalamy za pomocą funkcji glColor##(), którą musimy umieścić przed wywołaniem funkcji glVertex##() definiującymi punkty krańcowe linii. W powyższym programie wprowadź zmianę w pętli:
for(i = 0; i < 3; i++) { glTranslatef(0.0f,0.0f,-1.0f); // przesuwamy układ o 1 w tył glBegin(GL_LINES); glColor3f(1.0f,0.0f,0.0f); // pierwsza linia czerwona glVertex2f(-1.0f,-1.0f); // początek pierwszej linii glVertex2f( 1.0f, 1.0f); // koniec pierwszej linii glColor3f(0.0f,1.0f,0.0f); // druga linia zielona glVertex2f(-1.0f, 1.0f); // początek drugiej linii glVertex2f( 1.0f,-1.0f); // koniec drugiej linii glEnd(); }
Jeśli umieścisz wywołanie glColor##() przed wywołaniem glVertex##() dla obu końców linii, to OpenGL będzie rysowało linię z płynnym przejściem kolorów:
W programie zmień zawartość pętli:
for(i = 0; i < 3; i++) { glTranslatef(0.0f,0.0f,-1.0f); // przesuwamy układ o 1 w tył glBegin(GL_LINES); glColor3f(1.0f,0.0f,0.0f); // początek czerwony glVertex2f(-1.0f,-1.0f); // początek pierwszej linii glColor3f(1.0f,1.0f,0.0f); // koniec żółty glVertex2f( 1.0f, 1.0f); // koniec pierwszej linii glColor3f(0.0f,1.0f,0.0f); // początek zielony glVertex2f(-1.0f, 1.0f); // początek drugiej linii glColor3f(0.0f,0.0f,1.0f); // koniec niebieski glVertex2f( 1.0f,-1.0f); // koniec drugiej linii glEnd(); }
Szerokość linii ustalamy za pomocą funkcji
glLineWidth(GLfloat width);
Szerokość może być w granicach od 0.5 do 10.0 z krokiem co 0.125 (czyli zupełnie tak samo jak dla wielkości punktów). Funkcję glLineWidth() umieszczamy przed blokiem glBegin() ... glEnd(), w którym jest rysowana linia. Jeśli chcesz mieć linie o różnych szerokościach, to musisz użyć dla każdej z nich osobnego bloku glBegin() ... glEnd(), a przed każdym z tych bloków określić szerokość linii (tak samo postępowaliśmy w przypadku punktów).
W programie zmień zawartość pętli:
for(i = 0; i < 3; i++) { glTranslatef(0.0f,0.0f,-1.0f); // przesuwamy układ o 1 w tył glLineWidth(6.0f-2*i); // szerokość linii glBegin(GL_LINES); glColor3f(1.0f,0.0f,0.0f); // początek czerwony glVertex2f(-1.0f,-1.0f); // początek pierwszej linii glColor3f(1.0f,1.0f,0.0f); // koniec żółty glVertex2f( 1.0f, 1.0f); // koniec pierwszej linii glColor3f(0.0f,1.0f,0.0f); // początek zielony glVertex2f(-1.0f, 1.0f); // początek drugiej linii glColor3f(0.0f,0.0f,1.0f); // koniec niebieski glVertex2f( 1.0f,-1.0f); // koniec drugiej linii glEnd(); }
#include <math.h>
Następnie wprowadź kod do funkcji tmrAnimateTimer():
// Tutaj umieszczamy program dla OpenGL static GLfloat Tx = -2; // przesunięcie w osi OX static GLfloat Ty = -2; // przesunięcie w osi OY static GLfloat dTx = 0.02; // przyrost w osi OX static GLfloat dTy = 0.03; // przyrost w osi OY int i; glTranslatef(Tx,Ty,-10.0f); // przesuwamy układ w tył o 10 jednostek Tx += dTx; if(Tx < -2 || Tx > 2) dTx = -dTx; Ty += dTy; if(Ty < -2 || Ty > 2) dTy = -dTy; glBegin(GL_LINE_STRIP); // rysowanie łamanych otwartych // generujemy ciąg punktów spirali for(i = 0; i < 200; i++) glVertex3f(cos(i/10.0),sin(i/10.0),i/50.0); glEnd(); // Koniec kodu dla OpenGL
Następny program rysuje pięć okręgów jeden nad drugim w odległości co 0.5 jednostki na osi z.
// Tutaj umieszczamy program dla OpenGL static GLfloat Tx = -2; // przesunięcie w osi OX static GLfloat Ty = -2; // przesunięcie w osi OY static GLfloat dTx = 0.02; // przyrost w osi OX static GLfloat dTy = 0.03; // przyrost w osi OY int i,j; glTranslatef(Tx,Ty,-10.0f); // przesuwamy układ w tył o 10 jednostek Tx += dTx; if(Tx < -2 || Tx > 2) dTx = -dTx; Ty += dTy; if(Ty < -2 || Ty > 2) dTy = -dTy; for(j = 0; j < 5; j++) { glTranslatef(0.0f,0.0f,0.5f); // przesuwamy się do przodu o 0.5 jednostki glBegin(GL_LINE_LOOP); // rysowanie łamanych zamkniętych for(i = 0; i < 40; i++) glVertex2f(cos(6.28 * (i/40.0)),sin(6.28 * (i/40.0))); glEnd(); } // Koniec kodu dla OpenGL
// Tutaj umieszczamy program dla OpenGL static GLfloat Tx = -2; // przesunięcie w osi OX static GLfloat Ty = -2; // przesunięcie w osi OY static GLfloat dTx = 0.02; // przyrost w osi OX static GLfloat dTy = 0.03; // przyrost w osi OY glTranslatef(Tx,Ty,-6.0f); // przesuwamy układ w tył o 6 jednostek Tx += dTx; if(Tx < -2 || Tx > 2) dTx = -dTx; Ty += dTy; if(Ty < -2 || Ty > 2) dTy = -dTy; glBegin(GL_TRIANGLES); // rysowanie trójkątów glVertex2f( 0.0f, 0.0f); // pierwszy trójkąt glVertex2f(-1.0f, 0.0f); glVertex2f(-0.5f, 1.0f); glVertex2f( 0.0f, 0.0f); // drugi trójkąt glVertex2f( 1.0f, 0.0f); glVertex2f( 0.5f,-1.0f); glEnd(); // Koniec kodu dla OpenGL
Zmień zawartość bloku, aby dodać kolory:
glBegin(GL_TRIANGLES); // rysujemy trójkąty glColor3f(1.0f,1.0f,0.0f);// żółty glVertex2f( 0.0f, 0.0f); // pierwszy trójkąt glVertex2f(-1.0f, 0.0f); glVertex2f(-0.5f, 1.0f); glColor3f(0.0f,1.0f,1.0f);// cyjan glVertex2f( 0.0f, 0.0f); // drugi trójkąt glVertex2f( 1.0f, 0.0f); glVertex2f( 0.5f,-1.0f); glEnd();
Jeśli kolor określisz przed każdym wierzchołkiem, to wnętrze trójkąta będzie kolorowane gradientowo.
glBegin(GL_TRIANGLES); // rysujemy trójkąty glColor3f(1.0f,1.0f,0.0f);// żółty glVertex2f( 0.0f, 0.0f); // pierwszy trójkąt glColor3f(0.0f,1.0f,0.0f);// zielony glVertex2f(-1.0f, 0.0f); glColor3f(0.0f,0.0f,1.0f);// niebieski glVertex2f(-0.5f, 1.0f); glColor3f(1.0f,1.0f,0.0f);// żółty glVertex2f( 0.0f, 0.0f); // drugi trójkąt glColor3f(1.0f,0.0f,0.0f);// czerwony glVertex2f( 1.0f, 0.0f); glColor3f(0.0f,1.0f,1.0f);// cyjan glVertex2f( 0.5f,-1.0f); glEnd();
Trójkąty w programie znajdowały się na płaszczyźnie OXY. Nic jednakże nie stoi na przeszkodzie, aby były rysowane w przestrzeni 3D. Poniższy program rysuje trzy trójkąty, które tworzą ściany boczne czworościanu. Zwróć uwagę na kolejność określania wierzchołków trójkąta. Umówmy się na przyszłość, że będzie ona taka, aby patrząc na przód ściany, punkty były określane zgodnie z ruchem wskazówek zegara – jeśli będzie na odwrót, to OpenGL nie narysuje ściany. Zostało to określone w obsłudze zdarzenia onResize przy pomocy funkcji:
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
Opcja ta przyspiesza rysowanie sceny, ponieważ pomija ściany, które są odwrócone tyłem, a zatem niewidoczne dla obiektów pełnych (jeśli obiekty mają w ścianach dziury, przez które widać ich wnętrze, to pomijanie ścian należy wyłączyć). Więcej o tej opcji podamy na następnych zajęciach.
glBegin(GL_TRIANGLES); // rysujemy trójkąty glColor3f(1.0f,1.0f,0.0f); // trójkąt żółty glVertex3f( 0.0f, 1.0f, 0.0f); glVertex3f( 1.0f,-1.0f, 0.0f); glVertex3f( 0.0f, 0.0f, 1.0f); glColor3f(0.0f,1.0f,0.0f); // trójkąt zielony glVertex3f( 1.0f,-1.0f, 0.0f); glVertex3f(-1.0f,-1.0f, 0.0f); glVertex3f( 0.0f, 0.0f, 1.0f); glColor3f(1.0f,0.0f,1.0f); // trójkąt purpurowy glVertex3f(-1.0f,-1.0f, 0.0f); glVertex3f( 0.0f, 1.0f, 0.0f); glVertex3f( 0.0f, 0.0f, 1.0f); glEnd();
// Tutaj umieszczamy program dla OpenGL static GLfloat Tx = -2; // przesunięcie w osi OX static GLfloat Ty = -2; // przesunięcie w osi OY static GLfloat dTx = 0.02; // przyrost w osi OX static GLfloat dTy = 0.03; // przyrost w osi OY glTranslatef(Tx,Ty,-6.0f); // przesuwamy układ w tył o 6 jednostek Tx += dTx; if(Tx < -2 || Tx > 2) dTx = -dTx; Ty += dTy; if(Ty < -2 || Ty > 2) dTy = -dTy; glBegin(GL_QUADS); // rysujemy czworoboki glColor3f(1.0f,1.0f,0.0f); // czworobok czerwony glVertex3f( 1.0f, 0.0f, 0.0f); glVertex3f(-1.0f, 0.0f, 0.0f); glVertex3f(-1.0f, 1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 1.0f); glColor3f(0.0f,1.0f,0.0f); // czworobok zielony glVertex3f( 1.0f, 0.0f, 0.0f); glVertex3f( 1.0f,-1.0f, 1.0f); glVertex3f(-1.0f,-1.0f, 1.0f); glVertex3f(-1.0f, 0.0f, 0.0f); glEnd(); // Koniec kodu dla OpenGL
Nazwa stałej | Opis |
GL_POINTS |
Rysuje punkty |
GL_LINES |
Rysuje linie |
GL_LINE_STRIP |
Rysuje łamaną otwartą |
GL_LINE_LOOP |
Rysuje łamaną zamkniętą |
GL_TRIANGLES |
Rysuje trójboczną ścianę |
GL_QUADS |
Rysuje czworoboczną ścianę |
Wewnątrz bloku glBegin() ... glEnd() używamy funkcji glVertex##() do określenia współrzędnych punktów, które tworzą prymitywy.
glVertex2f(x,y) |
– | określa współrzędne punktu na płaszczyźnie OXY, Dla takiego wierzchołka współrzędna z jest równa 0. |
glVertex3f(x,y,z) |
– | określa współrzędne punktu w przestrzeni OXYZ. |
Wielkość punktów określa funkcja:
void glPointSize(GLfloat size);
Parametr size może przyjmować wartości od 0.5 do 10.0 z krokiem co 0.125. Funkcję tę należy wywołać przed blokiem glBegin() ... glEnd().
Grubość linii określa funkcja:
glLineWidth(GLfloat width);
Parametr width może przyjmować wartości od 0.5 do 10.0 z krokiem co 0.125. Funkcję tę należy wywołać przed blokiem glBegin() ... glEnd().
Do określania kolorów służy funkcja
glColor3f(r,g,b); |
Kolor jest definiowany przez trzy składowe,
które mogą przyjmować wartość od 0 do 1: r –
składowa czerwona (ang. Red) |
Funkcję można wywoływać tuż przed wywołaniem glVertex##(). Jeśli figura posiada kilka wierzchołków, to kolor będzie rysowany gradientowo przy przejściu z jednego wierzchołka do drugiego.
Układ współrzędnych przesuwamy za pomocą funkcji:
glTranslatef(GLfloat dx, GLfloat dy, GLfloat dz);
Po przesunięciu układu współrzędnych punkty będą rysowana w nowym miejscu przestrzeni. Przesunięcie nie wpływa na punkty już narysowane w innym bloku glBegin() ... glEnd().
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