0
Tutorial OpenGL i GLUT
Na podstawie ksi ki – OpenGL Ksi ga eksperta - Helion
SPIS TRE CI:
1. Szczegóły interfejsu .............................................................................................. 1
1.1. Typy danych ............................................................................................... 1
1.2. Konwencje nazewnictwa funkcji ............................................................... 2
2. Nasz pierwszy program......................................................................................... 4
3. Rysowanie kształtów w OpenGL.......................................................................... 7
4. Animacja w OpenGL i GLUT............................................................................... 13
4.1. Podwójne buforowanie............................................................................... 15
5. Maszyna stanów .................................................................................................... 15
5.1. Zapisywanie i odtwarzanie stanów ............................................................ 16
6. Rysowanie 3D ....................................................................................................... 17
6.1. Rysowanie punktów ................................................................................... 17
6.2. Ustalanie wielko ci punktów ..................................................................... 19
6.3. Rysowanie linii w trzech wymiarach ......................................................... 22
6.4. Linie łamane i zamkni te............................................................................ 22
6.5. Aproksymacja krzywych liniami prostymi ................................................ 23
6.6. Ustalanie szeroko ci linii ........................................................................... 23
6.7. Rysowanie trójk tów w trzech wymiarach ................................................ 24
6.7.1. Trójk t – mój pierwszy wielok t..................................................... 25
6.7.2. Nawini cie ....................................................................................... 25
6.7.3. Trójk ty sklejane ............................................................................. 26
6.7.4. Wachlarze trójk tów........................................................................ 27
6.8. Budowanie pełnych obiektów .................................................................... 27
6.9. Ustalenie koloru wielok ta......................................................................... 29
7. Tekstury................................................................................................................. 29
7.1. Ładowanie tekstur ...................................................................................... 29
7.2. Odwzorowywanie tekstur na obiekty geometryczne ................................. 32
7.3. Macierze tekstur ......................................................................................... 33
7.4. Prosty przykład dwuwymiarowy................................................................ 33
1
Szczegóły interfejsu
Biblioteka OpenGL została zaprojektowana przez uczonych ludzi posiadaj cych wiele do wiadczenia w projektowaniu
interfejsów programowania grafiki. Zastosowali oni standardowe reguły nazewnictwa funkcji i deklarowania zmiennych.
Powstałe API jest bardzo proste i przejrzyste, a na dodatek pozwala ró nym dostawcom w łatwy sposób rozbudowywa
bibliotek . OpenGL stara si nakłada jak najmniej zasad. Zasadami okre lane
s zało enia, jakie projektanci biblioteki poczynili w zakresie metod wykorzystania API przez u ytkowników.
Przykładami takich zasad mog by zało enia, e dane wierzchołków b d zawsze definiowane jako warto ci
zmiennoprzecinkowe, przed wł czeniem rendcringu mgła b dzie zawsze wł czona albo e wszystkie obiekty na scenie
obj te s tymi samymi parametrami o wietlenia. Takie zało enia mogłyby wyeliminowa wiele z popularnych technik
renderowania powstałych z biegiem czasu.
Typy danych
Aby ułatwi przenoszenie kodu korzystaj cego z OpenGL na inne platformy biblioteka definiuje swoje własne typy
danych. Typy te wynikaj z normalnych typów danych j zyka C, których mo na u ywa zamiennie, je eli zajdzie taka
potrzeba. Trzeba jednak pami ta , e ró ne kompilatory i rodowiska stosuj swoje własne zasady definiuj ce wielko i
uło enie w pami ci typów danych j zyka C. Stosowanie typów danych definiowanych przez OpenGL pozwala
odizolowa nasz kod od tego rodzaju zmian.
W tabeli 2.1 wypisano typy danych biblioteki OpenGL i odpowiadaj ce im typy danych j zyka C w 32-bitowych
rodowiskach Windows (Win32), a tak e przedrostki literałów. W tym tutorialu b dziemy u ywa tych przedrostków w
nazwach wszystkich literałów. Jak b dzie mo na zauwa y pó niej, te same przedrostki stosowane s równie w
nazwach wielu funkcji biblioteki OpenGL.
2
Wszystkie typy danych zaczynaj si od liter GL, co oznacza, e pochodz z biblioteki OpenGL. W wi kszo ci
przypadków za tymi literami znajduje si nazwa odpowiadaj cego im typu danych j zyka C (by te, short, int, float itd.).
Litera u umieszczona przed niektórymi z tych nazw informuje nas, e jest to typ danych bez znaku, na przykład ubyte
oznacza bajt bez znaku (unsigned byte). Do niektórych zastosowa przygotowano bardziej opisowe nazwy, takie jak size
do oznaczania warto ci długo ci lub gł boko ci. Na przykład typ Glsizei stosowany jest do oznaczania parametrów
rozrrtiaru opisywanego liczb całkowit . Oznaczenie clamp jest wskazówk , e warto po\yinna by ci ni ta w
zakresie do 0.0 - 1.0. Zmienne Glboolean u ywane s do przechowywania warto ci logicznych, typ GLenum
przeznaczony jest dla zmiennych wyliczeniowych, a GLbit field dla zmiennych zawieraj cych pola bitowe.
Wska niki i tablice nie otrzymały adnych specjalnych oznacze . Tablice dziesi ciu zmiennych typu GLshort
deklarowana jest po prostu jako:
GLshort shorts[10];
natomiast tablica dziesi ciu wska ników na warto ci GLdoubl e deklarowana jest nast puj co:
GLdouble *doubles[10];
Konwencje nazewnictwa funkcji
Dla wi kszo ci funkcji OpenGL zastosowano konwencje nazewnicze informuj ce nas, z jakiej biblioteki pochodzi dana
funkcja, a cz sto przekazuj ce te informacje o liczbie i typie pobieranych parametrów. Wszystkie funkcje posiadaj
rdze nazwy reprezentuj cy polecenie OpenGL odpowiadaj ce danej funkcji. Na przykład rdzeniem nazwy funkcji
glColor3f" jest polecenie Color. Przedrostek gl oznacza, e funkcja pochodzi z biblioteki gl, a przyrostek 3f mówi, e
funkcja pobiera trzy parametry zmiennoprzecin-kowe. Nazwy wszystkich funkcji OpenGL tworzone s zgodnie z
poni szym formatem:
<przedrostek bibliotcki><rdze polecenia><opcjonalna liczba parametrów> <opcjonalny typ parametrów>
Rysunek 2.4 przedstawia poszczególne elementy nazwy funkcji OpenGL. Przykładowa funkcja z przyrostkiem 3f
pobiera trzy parametry zmiennoprzecinkowe. Inne warianty pobieraj trzy liczby całkowite (glColor3i), trzy liczby
zmiennoprzecinkowe podwójnej precyzji (glColor3d) itd. Konwencja dodawania do nazwy funkcji liczby i typów para-
metrów (tabela 2.1) bardzo ułatwia zapami tanie listy parametrów funkcji bez konieczno ci przegl dania dokumentacji.
Niektóre wersje funkcji gl Color pobieraj te czwarty parametr, oznaczaj cy składow alfa (przezroczysto ).
Wiele kompilatorów C/C++ w systemach Windows zakłada, e ka da podawana jawnie warto
zmiennoprzecinkowajest typu double, chyba e zostanie podany inny typ. Stosuj c literały warto ci
zmiennoprzecinkowych z pomini ciem okre lenia, e s to warto ci typu float, a nie double, otrzymamy od kompilatora
ostrze enie mówi ce o tym, e próbujemy przekaza warto typu double do parametru typu float, co grozi utrat
precyzji liczby. Wraz z rozrastaniem si programów korzystaj cych z OpenGL liczba takich ostrze e szybko ro nie w
setki, co znacznie utrudnia wyszukiwanie rzeczywistych bł dów. Oczywi cie mo na wył czy te ostrze enia za pomoc
odpowiedniej opcji kompilatora, ale zdecydowanie odradzamy ten sposób załatwiania sprawy. Znacznie lepszym roz-
wi zaniem jest tworzenie przejrzystego i przeno nego kodu, dlatego nale y usuwa te ostrze enia poprzez
3
wyczyszczenie kodu (w tym przypadku, poprzez okre lanie typu float przy literałach warto ci zmiennoprzecinkowych),
a nie wył czanie potencjalnie przydatnych ostrze e .
Poza tym, mo na ulec pokusie u ywania wył cznie funkcji pobieraj cych liczby zmien-noprzecinkowe o podwójnej
precyzji i nie zawraca sobie głowy podawaniem typu ka dej wpisywanej jawnie warto ci. Niestety, biblioteka OpenGL
wewn trznie u ywa warto ci typu float, wi c u ywanie warto ci innych ni float powoduje zmniejszenie wydajno ci,
poniewa musz by one cały czas konwertowane na odpowiedni posta , zanim biblioteka b dzie mogła si nimi zaj
(nie wspominaj c nawet o tym, e zmienne typu double zajmuj dwa razy wi cej miejsca ni zmienne typu float). W
programie, w którym „przerzucanych" jest wiele takich liczb, mo e to mie znacz cy wpływ na wydajno !
4
Nasz pierwszy program
W celu lepszego poznania biblioteki GLUT przyjrzyjmy si teraz prawdopodobnie najmniejszemu na wiecie
programowi korzystaj cemu z OpenGL, który napisany został z wykorzystaniem biblioteki GLUT. Na poni szym
listingu przedstawiono program SIMPLE. Przy okazji dowiemy si kilku nowych rzeczy o bibliotece OpenGL!
#include <OpenGL.h>
// Funkcja wywoływana w celu narysowania sceny
void RenderScene(void)
{
// Wyczyszczenie okna aktualnym kolorem czyszcz cym
glClear(GL_COLOR_BUFFER_BIT);
/// Przekazanie polecenia czyszczenia do wykonania
glFlush();
}
// Ustalenie stanu rendrowania
void SetupRC(void)
{
glClearColor(0.0f,0.0f,1.0f,1.0f):
}
// Główny punki wej cia programu
void main(void)
{
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA):
glutCreateWi ndow("Si mple");
glutDi splayFunct RenderScene):
SetupRC():
glutMainLoo();
}
Program SIMPLE nie robi zbyt wiele. Po uruchomieniu z poziomu wiersza polece (lub rodowiska programistycznego)
tworzy tylko standardowe okno interfejsu GUI z nagłówkiem Simple i pustym niebieskim tłem. Je eli uruchomiony
został z poziomu Visual C++. to po jego zako czeniu w oknie konsoli pojawi si komunikat Press any key. Aby całko-
wicie zamkn program, konieczne b dzie naci ni cie dowolnego klawisza. Ta standardowa funkcja rodowiska
programistycznego Microsoftu u ywana w czasie uruchamiania programów konsolowych ma na celu umo liwienie nam
przejrzenia wszystkich komunikatów jakie mógł wygenerowa program, zanim jego okno zniknie z ekranu. Je eli ten
sam program uruchomimy z poziomu wiersza polece , to nie stwierdzimy takiego zachowania. Je eli klikniemy
dwukrotnie plik w Eksploratorze, to zobaczymy co prawda okno konsoli, ale zniknie ono w momencie zamkni cia
programu.
Ten prosty program korzysta z czterech funkcji biblioteki GLUT (oznaczonych przedrostkiem glut) i trzech
„prawdziwych" funkcji OpenGL (z przedrostkiem gl). Przejrzyjmy teraz program wiersz po wierszu, a pó niej
wprowadzimy do niego kilka dodatkowych funkcji i podniesiemy jako naszego pierwszego przykładu.
Nagłówek
Powy szy kod zawiera tylko jedn instrukcj wł czenia pliku nagłówkowego:
#include <0penGL.h>
Plik ten wł cza w siebie pliki nagłówkowe gl.h i glut.h, w których zapisane s prototypy funkcji u ywanych w programie.
Ciało
Nast pnie przejdziemy do punku wej cia wszystkich programów j zyka C:
void main(void)
Wykonywanie programów w C i C++ działaj cych w trybie konsoli zawsze rozpoczyna si od funkcji mai n.
Do wiadczeni programi ci tworz cy w systemie Windows mog zastanawia si , gdzie podziała si funkcja WinMain.
5
Nie ma jej, poniewa uruchamiana jest aplikacja konsolowa, wi c nie musimy tworzy adnego okna, ani obsługiwa
kolejki komunikatów. Za pomoc Win32 mo liwe jest tworzenie okien graficznych z poziomu aplikacji konsolowej,
podobnie jak mo na tworzy okna konsoli z poziomu aplikacji z interfejsem GUI. Wszystkie szczegóły zapisane s
wewn trz biblioteki GLUT. Trzeba pami ta , e ta biblioteka została zaprojektowana tak, eby ukrywa te wła nie
szczegóły działania platformy.
Tryb wy wietlania — pojedynczy bufor
W przedstawionej tutaj pierwszej linii kodu przekazujemy bibliotece GLUT informacj , jakiego trybu wy wietlania ma
u ywa w czasie tworzenia okna:
glutlnitDisplayMode(GLUT SINGLE | GLUTRGBA);
Podane tu znaczniki nakazuj bibliotece u ywa okna z pojedynczym buforem (GLUT_SINGLE) i trybu kolorów
RGBA (GLUT_RGBA). Okno z pojedynczym buforem oznacza, e wszystkie polecenia rysowania wykonywane s na
wy wietlanym wła nie oknie. Alternatyw jest okno z podwójnym buforowaniem, w którym polecenia rysowania
wykonywane s na nie wy wietlanym buforze, który jest nast pnie szybko przekazywany do widocznego okna. Ta
metoda jest cz sto stosowana do uzyskania efektu animacji, a jej zastosowanie demonstrowane jest w dalszej cz ci tego
rozdziału. Tak naprawd to trybu podwójnego buforowania b dziemy u ywa we wszystkich pozostałych przykładach
w tym tutorialu. Tryb koloru RGBA oznacza, e musimy osobno podawa intensywno ci wszystkich składowych koloru
— czerwonej (red), zielonej (green) i niebieskiej (blue). Alternatywnym trybem jest, aktualnie bardzo przestarzały, tryb
indeksowany, w którym podawany jest numer koloru we wcze niej zdefiniowanej palecie.
Tworzenie okna OpenGL
Nast pne wywołanie funkcji z biblioteki GLUT tworzy okno na ekranie. Poni sze polecenie tworzy nowe okno i nadaje
mu nagłówek Simple:
glutCreateWindow("Simple");
W jedynym parametrze funkcji glutCreateWindow przekazywany jest tekst nagłówka tworzonego okna.
Wy wietlaj ca funkcja zwrotna
W nast pnym wierszu naszego programu znajduje si wywołanie kolejnej funkcji biblioteki GLUT:
glutDisplayFunc(RenderScene);
W tej linii ustalamy, e zdefiniowana wcze niej funkcja RenderScene b dzie funkcj zwrotn {callback function)
wy wietlaj c obraz na ekranie. Oznacza to, e biblioteka GLUT b dzie wywoływa wskazan tu funkcj za ka dym
razem, gdy okno b dzie wymagało przerysowywania. Takie wywołanie nast pi przy pierwszym wy wietleniu okna, a
tak e przy zmianach jego rozmiaru albo odsłoni ciu. W tym wła nie miejscu umieszczamy wywołania funkcji
renderuj cych biblioteki OpenGL.
Ustalenie kontekstu i start
W nast pnym wierszu znajduje si polecenie niezwi zane ani z bibliotek GLUT, ani z OpenGL, ale jest to rozwi zanie,
którego b dziemy u ywa w całym tutorialu.
SetupRC();
W tej funkcji wykonujemy wszystkie inicjalizacje biblioteki OpenGL, jakie musz by zako czone przed rozpocz ciem
renderowania. Wiele ze stanów biblioteki OpenGL musi by ustalonych tylko raz i nie wymaga pó niejszych
modyfikacji przy renderowaniu ka dej ramki (przygotowywanej do wy wietlenia).
Ostatnie wywołaniem funkcji biblioteki GLUT znajduje si na ko cu programu:
glutMainLoop();
Ta funkcja uruchamia szkielet biblioteki GLUT. Po zdefiniowaniu funkcji zwrotnej obsługuj cej rysowanie na ekranie i
innych funkcji (powiemy o nich w dalszej cz ci) uruchamiany jest mechanizm biblioteki. Powrót z funkcji
glutMainLoop nast puje dopiero przy zako czeniu pracy programu, dlatego wystarczy wywoła j tylko raz. Funkcja ta
przetwarza wszystkie komunikaty systemu operacyjnego, naci ni cia klawiszy i inne, do czasu zako czenia pracy
6
programu.
Wywołania graficzne OpenGL
W funkcji SetupRC znajduje si wywołanie pojedynczej funkcji OpenGL:
glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
funkcja ta ustala kolor stosowany do czyszczenia zawarto ci okna. Prototyp tej funkcji wygl da nast puj co:
void glClearColor(GLclampf red. GLclampf green. GLclampf blue. GLclampf alpha);
W wi kszo ci implementacji biblioteki OpenGL typ GLclapf zdefiniowany jest jako float. W OpenGL pojedynczy kolor
reprezentowany jest jako mieszanka składowych, czerwonej, zielonej i niebieskiej. Warto ka dej z nich musi mie ci
si w zakresie od 0.0 do 1.0. Jest to metoda podobna do specyfikacji stosowanej w systemie Windows, według której
kolory tworzone s za pomoc makra RGB daj cego w wyniku warto COLORREF. Ró nica polega na tym, e w
systemie Windows ka da składowa koloru w strukturze COLORREF mo e przyjmowa warto ci od 0 do 255, co w
wyniku daje 256x256x256, czyli ponad 16 milionów kolorów. W bibliotece OpenGL warto ka dej składowej koloru
mo e przyjmowa dowoln warto zmiennoprzecinkow z zakresu od 0 do i. co oznacza, e teoretycznie mo na
zdefiniowa niesko czon liczb kolorów. W praktyce mo liwo ci odtwarzania kolorów w wi kszo ci urz dze
ograniczone s do 24 bitów, czyli 16 milionów kolorów.
Oczywi cie, zarówno system Windows, jak i biblioteka OpenGL pobieraj warto koloru i przekształcaj j
wewn trznie do najbli szego koloru mo liwego do uzyskania na dost pnym sprz cie wideo.
W tabeli 2.2 wypisali my kilka typowych kolorów i warto ci ich składowych. Mo na z nich korzysta w ka dej funkcji
OpenGL zwi zanej z obsług kolorów.
Ostatni parametr funkcji glClearColor to składowa alfa, która jest stosowana do mieszania kolorów oraz specjalnych
efektów w rodzaju przezroczysto ci. Przezroczysto obiektu jest własno ci pozwalaj c na przenikanie wiatła przez
obiekt. Przypu my, e chcieliby my przygotowa kawałek szkła zabarwionego na czerwono, za którym umieszczone
zostałoby niebieskie ródło wiatła. Niebieskie wiatło wpłyn łoby na wygl d czerwonej barwy szkła — mieszanka
kolorów niebieskiego i czerwonego daje w efekcie kolor purpurowy. Warto składowej alfa mo na zastosowa do
uzyskania półprze roczystego koloru czerwonego, który wygl dałby jak talia szkła, zza której widoczne byłyby inne
obiekty. Taki efekt wi e si nie tylko z wykorzystaniem składowej alfa, ale na razie warto składowej alfa b dziemy
ustala na 1.
7
Czyszczenie bufora kolorów
Do tej pory nakazali my bibliotece OpenGL u ywa koloru niebieskiego jako koloru czyszcz cego. W funkcji
RenderScene musimy teraz wykona wła ciw operacj czyszczenia:
glClear(GL_COLOR_BUFFER_BIT);
Funkcja glClear czy ci zawarto okre lonego bufora lub kombinacji buforów. Bufor jest obszarem w pami ci
przechowuj cym informacj o obrazie. Składowe czerwona, zielona i niebieska, tworz ce rysunek, zazwyczaj opisywane
s terminem bufora koloru (ang. color buffer) lub bufora pikseli (ang. pixel buffer).
W bibliotece OpenGL dost pnych jest kilka rodzajów buforów (koloru, gł bi, szablonowy i akumulacji). Na razie
wystarczy pami ta , e bufor koloru jest miejscem, w którym wewn trznie zapisywany jest wy wietlany obraz, a
czyszczenie tego bufora poleceniem glClear powoduje usuni cie z okna ostatniego obrazu.
Czyszczenie kolejki
W ko cu pojawia si ostatnie polecenie OpenGL:
glFlush();
Powoduje ono, e wszystkie niewykonane go tej pory polecenia OpenGL zostan wykonane. Jak na razie mamy tylko
jedno takie polecenie — glClear.
Biblioteka OpenGL wewn trznie korzysta z potoku renderuj cego sekwencyjnie przetwarzaj cego polecenia. Polecenia i
instrukcje OpenGL najcz ciej umieszczane s w kolejce do czasu, a sterownik OpenGL b dzie mógł przetworzy kilka
polece naraz. Takie działanie podnosi wydajno , poniewa komunikacja ze sprz tem jest z natury powolna. Jed-
nokrotne przesianie do karty graficznej wi kszej ilo ci danych jest znacznie szybsze ni wykonywanie kilku przesyłów,
po jednym dla ka dego polecenia lub instrukcji. W krótkim programie przedstawionym w powy szym listingu, funkcja
glFlush nakazuje bibliotece rozpocz cie przetwarzania dostarczonej do tej pory instrukcji, bez czekania na pojawianie si
kolejnych polece rysowania.
Program SIMPLE z cał pewno ci nie nale y do najciekawszych programów u ywaj cych biblioteki OpenGL, ale
dobrze obrazuje podstawy korzystania z okna za pomoc biblioteki GLUT, a tak e przedstawia sposób okre lania koloru
i czyszczenia zawarto ci okna. Teraz nieco rozbudujemy nasz program, dodaj c do niego kolejne funkcje z bibliotek
GLUT i OpenGL.
Rysowanie kształtów w OpenGL
Program SIMPLE tworzył puste okno z niebieskim tłem. Spróbujmy teraz co w tym oknie narysowa . Dodatkowo
spróbujemy uzyska mo liwo zmiany poło enia i rozmiaru okna, tak eby kod renderuj cy odpowiednio reagował na
te zmiany. Na poni szym listingu podano wszystkie potrzebne modyfikacje programu.
#include <OpenGL.h>
///////////////////////////////////////////////////////////
// Wywoływana w celu przerysowania sceny
void RenderScene(void)
{
// Wyczyszczenie okna aktualnym kolorem czyszcz cym
glClear(GL_COLOR_BUFFER_BIT);
// Aktualny kolor rysuj cy - czerwony
// R G B
glColor3f(1.0f, 0.0f, 0.0f);
// Narysowanie prostok ta wypełnionego aktualnym kolorem
glRectf(-25.0f, 25.0f, 25.0f, -25.0f);
// Wysłanie polece do wykonania
glFlush();
}
8
///////////////////////////////////////////////////////////
// Konfiguracja stanu renderowania
void SetupRC(void)
{
// Ustalenie niebieskiego koloru czyszcz cego
glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
}
///////////////////////////////////////////////////////////
// Wywoływana przez bibliotek GLUT w przypadku zmiany rozmiaru okna
void ChangeSize(int w, int h)
{
GLfloat aspectRatio;
// Zabezpieczenie przed dzieleniem przez zero
if(h == 0)
h = 1;
// Ustawienie wielko ci widoku na równ wielko ci okna
glViewport(0, 0, w, h);
// Ustalenie układu współrz dnych
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
// Wyznaczenie przestrzeni ograniczaj cej (lewy, prawy, dolny, górny, bliski, odległy)
aspectRatio = (GLfloat)w / (GLfloat)h;
if (w <= h)
glOrtho (-100.0, 100.0, -100 / aspectRatio, 100.0 / aspectRatio, 1.0, -1.0);
else
glOrtho (-100.0 * aspectRatio, 100.0 * aspectRatio, -100.0, 100.0, 1.0, -1.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
///////////////////////////////////////////////////////////
// Główny punkt wej cia programu
int main(int argc, char* argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
glutInitWindowSize(800, 600);
glutCreateWindow("GLRect");
glutDisplayFunc(RenderScene);
glutReshapeFunc(ChangeSize);
SetupRC();
glutMainLoop();
return 0;
}
Poprzednio nasz program wykonywał wył cznie operacj czyszczenia ekranu. Teraz do kodu rysuj cego dodali my
nast puj ce wiersze:
//Aktualny kolor rysuj cy - czerwony
//
RGB
glColor3f(1.0f, 0.0f, 0.0f);
// Narysowanie prostok ta wypełnionego aktualnym kolorem glRectf(-25.0f, 25.0f, 25.0f, -25.0f);
W tych wierszach, wywołaniem funkcji glColor3f ustalamy kolor przyszłych operacji rysowania (linii i wypełnie ), a
nast pnie funkcj glRectf rysujemy sam prostok t.
Funkcja glColor3f pozwala na takie same wybranie koloru jak funkcja glClearColor, cho nie wymaga podania warto ci
przezroczysto ci alfa. Przyjmowana jest domy lna warto 1.0, co oznacza brak przezroczysto ci:
void glColor3f(GLfloat red. GLfloat green . GLfloat blue):
Funkcja glRectf pobiera parametry zmiennoprzecinkowe, co jest sygnalizowane ko cow liter
r
. W nazwie funkcji nie jest
podawana liczba parametrów, poniewa wszystkie warianty polecenia glRect pobieraj cztery parametry. Podane tu cztery
parametry funkcji g i Rectf definiuj dwie pary współrz dnych—(x1, y1) i ( x 2 , y2):
void glRectf(GLfloat x l . GLfloat yl. GLfloat x2. GLfloat y2);
Pierwsza para definiuje lewy górny róg prostok ta, a druga para — prawy dolny.
9
W jaki sposób OpenGL przekłada te współrz dne na rzeczywiste pozycje w oknie? Wykonywane jest to wła nie w funkcji
zwrotnej ChangeSize. Funkcja ta zdefiniowana została jako funkcja zwrotna wywoływana za ka dym razem, gdy zmienia
si rozmiar okna (w momencie rozci gni cia, maksymalizacji itp.). Wyznaczenie tej funkcji odbywa si w bardzo podobny
sposób jak wyznaczenie wy wietlaj cej funkcji zwrotnej:
glutReshaceFunc(ChangeSize):
Za ka dym razem, gdy zmienia si wielko lub dowolny wymiar okna, konieczne jest ponowne ustalenie układu
współrz dnych.
Skalowanie do okna
W niemal wszystkich rodowiskach okienkowych u ytkownik mo e w dowolnym momencie zmieni wielko i wymiary
okna. Nawet je eli tworzymy gr cały czas działaj c na pełnym ekranie, to i tak okno raz zmienia swoj wielko — przy
uruchomieniu programu. W takim wypadku okno reaguje ponownym rozrysowaniem swojej zawarto ci, przy czym pod
uwag brane s nowe wymiary okna. Czasami mo e si zdarzy , e dla małych okien konieczne mo e by przyci cie obrazu,
a wi kszych oknach — wy wietlenie obrazu w jego oryginalnym rozmiarze. W naszych przykładach najcz ciej b dziemy
starali si tak przcskalowa rysunek, eby dostosowa go do wymiarów okna, niezale nie od wielko ci rysunku lub okna. W
efekcie w małym oknie znajdzie si pełny, cho niewielki rysunek, a w du ym oknie rysunek b dzie miał taki sam wygl d,
cho wi kszy rozmiar. Ten efekt bardzo cz sto mo na zobaczy w programach do rysowania, w których inne działanie ma
rozci ganie okna, a inne zmiana rozmiaru obrazka. Najcz ciej rozci gni cie okna nie powoduje zmiany rozmiaru obrazka,
ale powi kszenie obrazu sprawia, e i okno si powi ksza.
Ustawienie widoku i przestrzeni ograniczaj cej
Omawiali my ju sposób, w jaki widok i przestrze widoczna wpływaj na zakres współrz dnych i skalowanie rysunków
dwu- i trójwymiarowego w dwuwymiarowym oknie na ekranie komputera. Teraz zajmiemy si ustaleniem współrz dnych
widoku i przestrzeni ograniczaj cej w bibliotece OpenGL.
Mimo e nasz rysunek jest dwuwymiarowym płaskim prostok tem, to i tak rysujemy w przestrzeni współrz dnych
trójwymiarowych. Funkcja glRectf rysuje prostok t na płaszczy nie xy ze współrz dn z równ 0 (z=0). Nasza
perspektywa ustawiona jest wzdłu dodatnich warto ci osi z, tak eby widoczny był prostok t narysowany na zerowej
współrz dnej z.
Przy ka dej zmianie rozmiaru okna, widok i przestrze ograniczaj ca musz by zdefiniowane na nowo, tak aby dostosowa je
do nowych rozmiarów okna. W przeciwnym razie uzyskamy efekt podobny do przedstawionego na rysunku 2.7, w którym
odwzorowanie układu współrz dnych na współrz dne ekranowe pozostaje niezmienne bez wzgl du na zmiany rozmiarów
okna.
Ze wzgl du na to, e zmiany rozmiaru okna w ró nych rodowiska s wykrywane i obsługiwane odmiennie, biblioteka GLUT
wprowadza funkcj glutReshapeFunc rejestruj c funkcj zwrotn wywoływan przez bibliotek przy ka dej zmianie
10
rozmiarów okna. Funkcja przekazywana do glutReshapeFunc prototypowana jest w nast puj cy sposób:
void ChangeSize(GLsizei w, GLsizei h);
Wybrali my dla tej funkcji opisow nazw ChangeSize (zmie rozmiar), której konsekwentnie b dziemy u ywa tak e w
kolejnych przykładach.
Funkcja ChangeSize otrzymuje w parametrach now szeroko i wysoko okna, gdy tylko zmieni si jego rozmiar. Mo emy
u y tych informacji do zmodyfikowania odwzorowania potrzebnego nam układu współrz dnych na współrz dne
rzeczywistego ekranu. Operacj t wykonamy za pomoc dwóch funkcji: glViewport i glOrtho.
Definiowanie widoku
Aby zrozumie metod uzyskiwania definicji widoku, musimy dokładniej przyjrze si zawarto ci funkcji ChangeSize.
Na pocz tku wywoływana jest w niej funkcja glViewport z podanymi nowymi warto ciami szeroko ci i wysoko ci okna.
Funkcja glViewport zdefiniowana jest nast puj co:
void glViewport(GLint x, Glint y, GLsizei width. GLsizei height):
Parametry x i y okre laj pozycj dolnego lewego rogu widoku wewn trz okna, a parametry width i height definiuj
rozmiar tego widoku w pikselach. Najcz ciej warto ci parametrów x i y ustalane s na 0, ale widoków mo na u ywa te
do renderowania kilku rysunków w ró nych obszarach okna. Widok definiuje obszar wewn trz okna podawany we
współrz dnych ekranu, którego OpenGL mo e u ywa do rysowania (zobacz rysunek 2.8). Nast pnie aktualna przestrze
ograniczaj ca odwzorowywana jest w nowym widoku. Je eli zdefiniujemy widok mniejszy od współrz dnych okna, to
renderowany obraz po przeskalowaniu równie b dzie mniejszy, co wida na rysunku 2.8.
Definiowanie widocznej przestrzeni ograniczaj cej
Ostatnim wymaganiem w stosunku do naszej funkcji ChangeSize jest ponowne zdefiniowanie przestrzeni ograniczaj cej
w taki sposób, aby współczynnik kształtu obrazu (aspect ratio) pozostał niezmieniony. Współczynnik kształtu obrazu to
stosunek liczby pikseli w jednostce długo ci w kierunku pionowym, do liczby pikseli w jednostce długo ci w kierunku
poziomym. Współczynnik kształtu obrazu o warto ci 1.0 definiuje obraz kwadratowy, natomiast współczynnik o warto ci
0.5 definiuje obraz, w którym na jeden piksel w jednostce długo ci w kierunku pionowym przypadaj dwa piksele w tej
samej jednostce długo ci w kierunku poziomym.
Je eli zdefiniujemy niekwadratowy widok, w którym odwzorowywana jest kwadratowa przestrze ograniczaj ca, to
otrzymamy obraz zniekształcony. Na przykład widok o wymiarach identycznych z rozmiarami okna, w którym
odwzorowywana jest kwadratowa przestrze ograniczaj ca, mo e w w skich, ale wysokich oknach przedstawia w skie i
wysokie obrazy, a w oknach niskich i szerokich — obrazy niskie i szerokie. W takim przypadku narysowany przez nas
kwadrat b dzie faktycznie wygl dał jak kwadrat tylko wtedy, gdy rozmiar okna b dzie kwadratem.
W naszym przykładzie do przestrzeni ograniczaj cej zastosowali my rzutowanie prostopadle. W bibliotece OpenGL
funkcj tworz c takie rzutowanie jest funkcja glOrtho:
void glOrtho(GLdouble left. GLdouble right. GLdouble bottom, GLdouble top, GLdouble near, GLdouble far);
W trójwymiarowej przestrzeni kartezja skiej warto ci left i right okre laj minimaln i maksymaln współrz dn na osi
x, a warto ci bottom i top — na osi y. Warto ci near i far dotycz osi z, przy czym najcz ciej warto ci ujemne rosn
11
wraz z oddalaniem si od widza (rysunek 2.9). Wiele bibliotek graficznych i rysunkowych w poleceniach ryso-wania
u ywa współrz dnych okna (pikseli). Nowym u ytkownikom biblioteki OpenGL bardzo trudno si przyzwyczai do
korzystania ze współrz dnych zmiennoprzecinkowych (sprawiaj cych wra enie z niczym niezwi zanych), jednak po
napisaniu kilku programów takie działanie staje si drug natur programisty.
Prosz zwróci uwag na dwie funkcje umieszczone tu przed wywołaniem funkcji glOrtho:
// Ustalenie układu współrz dnych
glMatrixMode(GL_PROJECTION); glLoadldentity();
Macierz rzutowania jest miejscem, w którym rzeczywi cie definiujemy nasz przestrze widoczn . Pojedyncze
wywołanie funkcji glLoadIdentit.y jest konieczne, poniewa funkcja glOrtho tak naprawd nie tworzy przestrzeni
ograniczaj cej, ale modyfikuje istniej c przestrze . Mno y ona macierz opisuj c aktualn przestrze ograniczaj c
przez macierz opisuj c przestrze ograniczaj c podan w parametrach. Na razie wystarczy zapami ta , e funkcja
glLoadIdentity słu y do ponownego ustawienia (resetu) układu współrz dnych, zanim wykonane zostan jakiekolwiek
manipulacje na macierzach. Bez ponownego ustawienia ukfadu współrz dnych przed ka dym wywołaniem funkcji
glOrtho ka de kolejne wywołanie powodowałoby coraz wi ksze zniekształcenia planowanej przestrzeni ograniczaj cej,
co w efekcie mogłoby nawet doprowadzi do znikni cia kwadratu z ekranu.
Ostatnie dwa przedstawione tutaj wiersze kodu informuj bibliotek OpenGL, e wszystkie przyszłe przekształcenia b d
dotyczyły naszych modeli, czyli tego, co narysujemy:
glMatrixMode(GL_MODELVIEW);
glLoadldentity();
Musimy jednak ju teraz poda informacje o sposobie konfigurowania tych operacji ich warto ciami domy lnymi. Je eli
tego nie zrobimy i pozwolimy na eksperymentowanie, to wy wietlany przez program obraz mo e by daleki od naszych
oczekiwa .
Aby kwadrat był kwadratem
Poni szy kod zajmuje si utrzymaniem „kwadratowo ci" naszego kwadratu:
// Wyznaczenie przestrzeni ograniczaj cej (lewy, prawy, dolny, górny, bliski, odległy)
aspectRatio = (GLfloat)w / (GLfloat)h:
if (w <= h)
glOrtho (-100.0, 100.0, -100 / aspectRatio, 100.0 / aspectRatio, 1.0, -1.0);
else
glOrtho (-100.0 * aspectRatio, 100.0 * aspectRatio, -100.0, 100.0, 1.0, -1.0):
Nasza przestrze ograniczaj ca (widoczny układ współrz dnych) modyfikowana jest w ten sposób, e lewa strona
zawsze ma warto x=-100, a prawa strona rozci ga si do warto ci 100, chyba e szeroko okna jest wi ksza od jego
wysoko ci. Na podobnej zasadzie dół przestrzeni ograniczaj cej zawsze ma warto y=-100, a góra rozci ga si do
12
warto ci 100, chyba e wysoko okna jest wi ksza od jego szeroko ci. W takim przypadku, górna współrz dna równie
skalowana jest współczynnikiem kształtu ekranu. W ten sposób, niezale nie od kształtu okna, utrzymywany jest
kwadratowy region o wymiarach 200x200, którego rodek ma współrz dne (0.0). Rysunek 2.10 przedstawia zasad
działania tego mechanizmu.
13
Animacja w OpenGL i GLUT
Jak na razie omawiali my podstawy u ytkowania biblioteki GLUT w zakresie tworzenia okien i korzystania z rysuj cych
polece OpenGL. Bardzo cz sto b dziemy chcieli przesun nasz scen albo j obróci , tworz c w ten sposób efekt
animacji. We miemy teraz poprzedni przykład z rysowanym kwadratem i sprawimy, e b dzie si on odbijał od ram
okna. Mo emy przygotowa p tl , w której b dziemy zmienia współrz dne obiektu przed wywołaniem funkcji
RenderScene. Spowoduje to, e kwadrat b dzie sprawiał wra enie ruchu wewn trz okna.
Biblioteka GLUT pozwala na zarejestrowanie specjalnej funkcji zwrotnej ułatwiaj cej wykonywanie prostych sekwencji
animacji. Umo liwia to funkcja glutTimerFunc, pobieraj ca nazw funkcji zwrotnej i interwał, z jakim ta funkcja ma
by wywoływana:
void glutTimerfunc(unsigned int msecs, void (*func)(int value). int value);
Funkcja ta nakazuje bibliotece GLUT odczekiwa msecs milisekund przed ka dym wywołaniem funkcji func. W
parametrze vdlue mo na do tej funkcji przekazywa zdefiniowane przez u ytkownika warto ci. Funkcja wywoływana co
okre lony czas prototypowana jest w nast puj co:
void TimerFunction(int value);
Przeciwnie ni w mechanizmach czasowych systemu Windows, funkcja ta wywoływana jest tylko raz. Aby uzyska efekt
ci głej animacji w samej funkcji czasowej, musimy ponownie ustawi mechanizm czasowy.
W programie GLRect zmienimy teraz zapisane na stałe warto ci pozycji naszego prostok ta, zast puj c je zmiennymi, a
nast pnie b dziemy cały czas modyfikowa te zmienne w wywoływanej cyklicznie funkcji czasowej. W ten sposób
prostok t zacznie porusza si wewn trz okna. Przyjrzyjmy si przykładowi tego rodzaju animacji. Na poni szym
listingu znajduje si zmodyfikowany program z listingu poprzedniego, do którego dodano funkcj animacji kwadratu
odbijaj cego si od ram okna. Musimy cały czas kontrolowa pozycj i wielko kwadratu, jak równie wszystkie
zmiany wielko ci okna.
#include <OpenGL.h>
// Pocz tkowy rozmiar i pozycja prostok ta
GLfloat x = 0.0f;
GLfloat y = 0.0f;
GLfloat rsize = 25;
// Rozmiar kroku (liczba pikseli) w osi x i y
GLfloat xstep = 1.0f;
GLfloat ystep = 1.0f;
// Dane zmieniaj cych si rozmiarów okna
GLfloat windowWidth;
GLfloat windowHeight;
///////////////////////////////////////////////////////////
// Wywoływana w celu przerysowania sceny
void RenderScene(void)
{
// Wyczyszczenie okna aktualnym kolorem czyszcz cym
glClear(GL_COLOR_BUFFER_BIT);
// Aktualny kolor rysuj cy - czerwony
// R G B
glColor3f(1.0f, 0.0f, 0.0f);
// Narysowanie prostok ta wypełnionego aktualnym kolorem
glRectf(-25.0f, 25.0f, 25.0f, -25.0f);
// Wysłanie polece do wykonania
glFlush();
}
///////////////////////////////////////////////////////////
// Wywoływana przez bibliotek GLUT w czasie, gdy okno nie
// jest przesuwane ani nie jest zmieniana jego wielko
void TimerFunction(int value)
{
// Odwrócenie kierunku, je eli osi gni to lew lub praw kraw d
if(x1 > windowWidth-rsize || x1 < -windowWidth)
xstep = -xstep;
// Odwrócenie kierunku, je eli osi gni to doln lub górn kraw d
if(y1 > windowHeight || y1 < -windowHeight + rsize)
ystep = -ystep;
14
// Wykonanie przesuni cia kwadratu
x1 += xstep;
y1 += ystep;
// Kontrola obramowania. Wykonywana jest na wypadek, gdyby okno
// zmniejszyło swoj wielko w czasie, gdy kwadrat odbijał si od
// kraw dzi, co mogłoby spowodowa , e znalazł by si poza
// przestrzeni ograniczaj c .
if(x1 > (windowWidth-rsize + xstep))
x1 = windowWidth-rsize-1;
else if(x1 < -(windowWidth + xstep))
x1 = -windowWidth -1;
if(y1 > (windowHeight + ystep))
y1 = windowHeight-1;
else if(y1 < -(windowHeight - rsize + ystep))
y1 = -windowHeight + rsize - 1;
// Ponowne rysowanie sceny z nowymi współrz dnymi
glutPostRedisplay();
glutTimerFunc(33,TimerFunction, 1);
}
///////////////////////////////////////////////////////////
// Konfigurowanie stanu renderowania
void SetupRC(void)
{
// Ustalenie niebieskiego koloru czyszcz cego
glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
}
///////////////////////////////////////////////////////////
// Wywoływana przez bibliotek GLUT przy ka dej zmianie wielko ci okna
void ChangeSize(GLsizei w, GLsizei h)
{
GLfloat aspectRatio;
// Zabezpieczenie przed dzieleniem przez zero
if(h == 0)
h = 1;
// Ustalenie wielko ci widoku zgodnego z rozmiarami okna
glViewport(0, 0, w, h);
// Ustalenie układu współrz dnych
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
// Ustanowienie przestrzeni ograniczaj cej (lewo, prawo, dół, góra, blisko, daleko)
aspectRatio = (GLfloat)w / (GLfloat)h;
if (w <= h)
{
windowWidth = 100;
windowHeight = 100 / aspectRatio;
glOrtho (-100.0, 100.0, -windowHeight, windowHeight, 1.0, -1.0);
}
else
{
windowWidth = 100 * aspectRatio;
windowHeight = 100;
glOrtho (-windowWidth, windowWidth, -100.0, 100.0, 1.0, -1.0);
}
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
///////////////////////////////////////////////////////////
// Główny punkt wej cia programu
void main(void)
{
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
glutCreateWindow("Bounce");
glutDisplayFunc(RenderScene);
glutReshapeFunc(ChangeSize);
glutTimerFunc(33, TimerFunction, 1);
SetupRC();
glutMainLoop();
}
15
Podwójne buforowanie
Jedn z najwa niejszych funkcji ka dego pakietu graficznego jest obsługa podwójnego buforowania. Funkcja ta pozwala
na wykonywanie kodu rysuj cego na buforze niewidocznym na ekranie. Nast pnie poleceniem podmiany narysowany w
ten sposób obraz umieszczany jest natychmiast na ekranie.
Mechanizm podwójnego buforowania ma słu y dwóm celom. Po pierwsze, rysowanie pewnych bardziej
skomplikowanych rysunków mo e zaj do du o czasu, co mo e powodowa , e na ekranie widoczne b d kolejne
kroki tworzenia obrazu. Za pomoc podwójnego buforowania obraz mo e by najpierw przygotowany, a dopiero pó niej
wy wietlony na ekranie. W efekcie u ytkownik nigdy nie b dzie miał okazji zobaczy cz ciowo utworzonego rysunku.
Drugim zastosowaniem podwójnego buforowania jest animacja. Ka da ramka rysowana jest w niewy wietlanym buforze,
a nast pnie szybko umieszczana na ekranie poprzez zamian buforów — widocznego i niewidocznego. Biblioteka GLUT
pozwala na u ywanie okien z podwójnym buforowaniem. Prosz na powy szym listingu zauwa y nast puj cy wiersz:
glutInitDIsplayMode(GLUT_DOUBLE | GLUT_RGB);
Zmienili my stał GLUT_SINGLE na GLUT_DOUBLE. Ta zmiana powoduje, e wszystkie polecenia działaj na buforze
niewidocznym.
Nast pnie, zmienili my te ko cówk funkcji RenderScene:
// Wysłanie polece do wykonania i zamiana buforów
glutSwapBuffers();
Terazju nie wywołujemy funkcji glFlush. Nie jest ona ju potrzebna, poniewa zamiana buforów powoduje niejawne
wykonania operacji czyszczenia bufora polece .
Wprowadzone zmiany tworz w oknie płynn animacj kwadratu odbijaj cego si od kraw dzi okna. Funkcja
glutSwapBuffers powoduje wyczyszczenie bufora polece , nawet je eli działaliby my w trybie pojedynczego buforo-
wania. Aby zobaczy , jak ta sama animacja wygl da b dzie w trybie buforowania pojedynczego, wystarczy w
programie zmieni stał GLUT_DOUBLE na GLUT_SINGLE. Jak mo na zauwa y , kwadrat cały czas zacina si i
migocze — w trybie pojedynczego buforowania efektu animacji nie mo na nazwa zadowalaj cym.
Biblioteka GLUT stanowi wzgl dnie prost podstaw dla tworzenia zło onych programów przykładowych, a nawet
rozbudowanych aplikacji komercyjnych (zakładaj c, e nie potrzeba nam funkcji systemu operacyjnego i interfejsu
GUI). Zadaniem tego tutorialu nie jest jednak opisywanie biblioteki GLUT w całej jej wielko ci. B dziemy ogranicza
si do opisywania jedynie niewielkich wycinków tej biblioteki, koniecznych do zademonstrowania ró nych funkcji
biblioteki OpenGL.
Maszyna stanów OpenGL
Rysowanie grafik trójwymiarowych to zadanie bardzo zło one. W kolejnych rozdziałach opisywa b dziemy ró ne
funkcje biblioteki OpenGL. Wiele ró nych efektów mo e wpływa na sposób rysowania ka dego wycinka geometrii.
Czy pada na niego wiatło? Jakie s wła ciwo ci tego wiatła? Jakie s wła ciwo ci materiału? Jak tekstur nale y
zastosowa ? Czy w ogóle stosowa jak kolwiek tekstur ? List takich pyta mo na by jeszcze długo rozbudowywa .
T kolekcj zmiennych nazywamy stanem potoku. Maszyna stanów jest abstrakcyjnym modelem kolekcji zmiennych
stanu, z których ka da mo e przyjmowa ró ne warto ci, by wł czana lub wył czana itd. Okre lanie wszystkich
zmiennych stanu, za ka dym razem gdy chcemy co narysowa , nie jest praktyczne, dlatego biblioteka OpenGL
wprowadza model stanów (lub maszyn stanów) zapami tuj cy warto ci wszystkich zmiennych stanu. Gdy jakiej
zmiennej nadawana jest warto , to pozostaje ona niezmieniona do czasu, a zmieni j jaka inna funkcja. Wiele
zmiennych stanów jest po prostu wł czana i wył czana. Na przykład o wietlenie mo na wł cza i wył cza . Obiekty
rysowane bez wł czonego o wietlenia s rysowane bez wykonywania jakichkolwiek oblicze o wietlenia wpływaj cego
na zcsiaw kolorów zastosowany w obiekcie. Ten sam obiekt rysowany po wł czeniu o wietlenia rysowany jest z
zastosowaniem oblicze o wietlenia.
Aby wł czy ten rodzaj zmiennych stanu, nale y u y nast puj cej funkcji OpenGL:
void glEnable(GLenum capability);
16
T sam zmienn mo na pó niej wył czy za pomoc nast puj cej funkcji:
void glDisable(Glenum capability);
W przypadku o wietlenia, poni sz instrukcj mo na wł czy odpowiedni stan:
glEnable(GL_LIGHTING);
Wył czanie tego stanu odbywa si poprzez wywołanie poni szej instrukcji:
glDisable(GL_LIGHTING);
Je eli chcieliby my sprawdzi , czy jaka zmienna stanu jest wł czona, to biblioteka OpenGL udost pnia nam do tego celu
odpowiedni funkcj :
GLboolean glIsEnabled(GLenum capability);
Jednak nic wszystkie zmienne mog by tylko wł czane i wył czane. Wiele innych funkcji OpenGL, które dopiero b dziemy
opisywa , mo e przyjmowa warto ci zapami tywane do czasu ich zmiany. Mo emy te w dowolnym momencie
sprawdzi , jakie warto ci przechowywane s w tych zmiennych. Dost pny jest w tym celu zestaw funkcji pozwalaj cych na
sprawdzenie warto ci logicznych, całkowitych, zmiennoprzecinkowych pojedynczej oraz podwójnej precyzji. Prototypy tych
czterech funkcji wygl daj nast puj co:
void glGetBooleanv(GLenum pname. GLboolean *params);
void glGetDoublev(GLenum pname, GLdouble *params);
void glGetFloatv(GLenum pname, GLfloat *params);
void glGetIntegerv(GLenum pname. GLint *params);
Ka da z funkcji mo e zwróci pojedyncz warto lub cał tablic warto ci, które zapisywane s pod podanym adresem.
Wi kszo z tych funkcji z pewno ci nie b dzie od razu zrozumiała, ale wraz z do wiadczeniem ro nie równie zrozumienie.
Zapisywanie i odtwarzanie stanów
Biblioteka OpenGL posiada równie bardzo wygodny mechanizm zapisywania i odtwarzania całych zakresów warto ci
stanu. Stos jest wygodn struktur danych pozwalaj c na odkładanie warto ci (push) i pó niejsze zdj cie ich ze stosu (pop).
Elementy s zdejmowane ze stosu w kolejno ci odwrotnej do kolejno ci kładzenia ich na stosie. Tak struktur nazywa si
UFO (ang. Last In First Out — ostatni przyszedł, pierwszy wyjdzie). Mo na j prosto opisa w nast puj cy sposób: mówi c:
„Prosz , zachowaj to dla mnie", zapisujemy dane na stos, a pó niej, mówi c: „Oddaj mi to, co wła nie zapisałem", zdej-
mujemy dane ze stosu. Idea stosu odgrywa znacz c rol w manipulacjach macierzami.
Poni szym poleceniem mo emy zapisa na stos atrybutów zarówno pojedyncz warto stanu, jak i cały ich zbiór:
void glPushAttrib(GLbitfield mask);
Odpowiednio, te same warto ci mog by odczytane za pomoc tego polecenia:
void glPopAttrib(GLbitfield mask):
Prosz zauwa y , e parametrem tych funkcji jest warto typu bitfield (pole bitowe). Oznacza to, e musimy stosowa
mask bitow , która pozwala na wykonywanie bitowej operacji sumy logicznej (OR — w j zyku C reprezentowanej przez
operator |), dzi ki czemu w jednym wywołaniu funkcji mo na zapisa kilka warto ci zmiennych stanu. Na przykład mo emy
zapisa stan o wietlenia i teksturowania, wywołuj c nast puj ce polecenie:
glPushAttrib(GL_TEXTURE_BIT | GL_LIGHTING_BIT);
17
Rysowanie 3D
Rysowanie punktów
Zaczniemy od pierwszych i najprostszych obiektów podstawowych — punktów. Przyjrzyjmy si poni szemu kodowi:
glBegin(GL_POINTS);
//Obiektami podstawowymi b d punkty
glVertex3f(0.0f. O.0f. O.Of):
//Definiujemy punkt
glVertex3f(50.0f, 50.Of, 50. 0f);
//Definiujemy kolejny punkt
g lEnd ():
// Koniec rysowania punktów
Parametr funkcji glBegin — GL_POINTS — nakazuje bibliotece OpenGL interpretowa wszystkie podawane
wierzchołki jako punkty do rysowania. W przykładzie zdefiniowano dwa wierzchołki, które przekładaj si na dwa
punkty rysowane na ekranie.
Ten przykład uwydatnia bardzo wa n rzecz, o której trzeba pami ta , stosuj c funkcje glBegin i glEnd — mi dzy ich
wywołaniami mo emy podawa cał list obiektów podstawowych pod warunkiem, e s one tego samego typu. W ten
sposób jedn sekwencj funkcji glBegin i glEnd mo emy doda do sceny dowoln liczb obiektów podstawowych.
Poni szy segment kodu dla odmiany b dzie wykonywał si znacznie wolniej ni podany wcze niej:
gl Begm(GL_POINTS);
// Definiujemy punkt do rysowania
glVertex3f(0.0f, 0.0f, 0.0f);
glEnd();
g1Begin(GL_POINTS):
// Definiujemy kolejny punkt
glVertex3f(50 0f, 50.0f, 50.0f);
glEnd() ;
Nasz pierwszy przykład
Kod z poni szego listingu rysuje w rodowisku trójwymiarowym kilka punktów. Program stosuje proste
obliczenia trygonometryczne do wyznaczenia pozycji serii punktów tworz cych co w rodzaju korkoci gu
wokół osi z. Kod pochodzi z programu POINTS, który mo na pobra wraz z tym tutorialem. Nale y zauwa y ,
e w funkcji SetupRC ustalamy aktualny kolor rysuj cy na zielony.
#include <math.h>
// Definiujemy stał o warto ci liczby PI
#define GL_PI 3.1415f
// Wielko obrotów
static GLfloat xRot = 0.0f;
static GLfloat yRot = 0.0f;
// Wywoływana w celu przerysowania sceny
void RenderScene(void)
{
GLfloat x,y,z,angle; // Zmienne przechowuj ce współrz dne i k ty
// Wyczyszczenie okna aktualnym kolorem czyszcz cym
glClear(GL_COLOR_BUFFER_BIT);
// Zapisanie stanu macierzy i wykonanie obrotu
glPushMatrix();
glRotatef(xRot, 1.0f, 0.0f, 0.0f);
glRotatef(yRot, 0.0f, 1.0f, 0.0f);
// Wywoła tylko raz, przed rysowaniem wszystkich punktów
glBegin(GL_POINTS);
z = -50.0f;
for(angle = 0.0f; angle <= (2.0f*GL_PI)*3.0f; angle += 0.1f)
{
x = 50.0f*sin(angle);
y = 50.0f*cos(angle);
18
// Okre lenie punktu i przesuni cie współrz dnej Z
glVertex3f(x, y, z);
z += 0.5f;
}
// Zako czenie rysowania punktów
glEnd();
// Odtworzenie macierzy przekształce
glPopMatrix();
// Wykonanie polece rysowania
glutSwapBuffers();
}
// Ta funkcja wykonuje wszystkie konieczne inicjalizacje kontekstu renderowania
void SetupRC()
{
// Czarne tło
glClearColor(0.0f, 0.0f, 0.0f, 1.0f );
// B dziemy rysowa kolorem zielonym
glColor3f(0.0f, 1.0f, 0.0f);
}
void SpecialKeys(int key, int x, int y)
{
if(key == GLUT_KEY_UP)
xRot-= 5.0f;
if(key == GLUT_KEY_DOWN)
xRot += 5.0f;
if(key == GLUT_KEY_LEFT)
yRot -= 5.0f;
if(key == GLUT_KEY_RIGHT)
yRot += 5.0f;
if(key > 356.0f)
xRot = 0.0f;
if(key < -1.0f)
xRot = 355.0f;
if(key > 356.0f)
yRot = 0.0f;
if(key < -1.0f)
yRot = 355.0f;
// Od wie enie zawarto ci okna
glutPostRedisplay();
}
void ChangeSize(int w, int h)
{
GLfloat nRange = 100.0f;
// Zabezpieczenie przed dzieleniem przez zero
if(h == 0)
h = 1;
// Ustalenie wymiarów widoku na zgodnych z wymiarami okna
glViewport(0, 0, w, h);
// Ponowne ustawienie stosu macierzy rzutowania
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
// Utworzenie przestrzeni ograniczaj cej (lewo, prawo, dół, góra, blisko, daleko)
if (w <= h)
glOrtho (-nRange, nRange, -nRange*h/w, nRange*h/w, -nRange, nRange);
else
glOrtho (-nRange*w/h, nRange*w/h, -nRange, nRange, -nRange, nRange);
// Ponowne ustawienie stosu macierzy rzutowania
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
int main(int argc, char* argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
19
glutCreateWindow("Points Example");
glutReshapeFunc(ChangeSize);
glutSpecialFunc(SpecialKeys);
glutDisplayFunc(RenderScene);
SetupRC();
glutMainLoop();
return 0;
}
W tym i pozostałych przykładach z tego rozdziału dla naszych celów wa ny jest jedynie kod pomi dzy
wywołaniami funkcji glBegin i glEnd. Powy szy kod wylicza współrz dne x i y na podstawie k ta trzykrotnie
rosn cego od warto ci 0° do 360°. W programie przedstawiamy te warto ci w radianach, a nie w stopniach; kto nie
zna trygonometrii, musi uwierzy na słowo. Przy rysowaniu ka dego punktu warto współrz dnej z powi kszana jest
o niewielk warto . Po uruchomieniu programu zobaczymy na ekranie koło składaj ce si z samych punktów.
Wynika to z tego, e pocz tkowo b dziemy patrze wzdłu osi z. Aby zobaczy cał spiral , nale y naciskaj c
klawisze strzałek, obróci rysunek wokół osi x i y.
Ustalanie wielko ci punktu
W czasie rysowania pojedynczego punktu domy ln wielko ci tego punktu jest jeden piksel. Mo na to zmieni , stosuj c
funkcj glPointSize:
void glPointSize(GLfloat size):
Funkcja glPointSize pobiera jeden parametr okre laj cy przybli on wielko rysowanego punktu podan w
pikselach. Niestety, nie mo na poda dowolnego rozmiaru punktu, dlatego nale y si upewni , czy podawany
rozmiar b dzie prawidłowo obsłu ony. Mo na u y poni szego kodu w celu sprawdzenia zakresu dost pnych
rozmiarów punktów i najmniejszych odst pów mi dzy nimi.
GLfloat sizes[2];
// Do zapisania dopuszczalnego zakresu wielko ci
GLfloat step;
//Zapami tuje obsługiwany przyrost rozmiaru
// Pobranie dopuszczalnych rozmiarów punktów
glGetFloatv(GL_POINT_SIZE_RANGE, s i z e s ) ;
glGetFloatv(GL_POINT_SIZE_GRANULARITY, &step);
W tablicy sizes znajd si dwa elementy okre laj ce najmniejsz i najwi ksz warto przyjmowan przez funkcj gl PointSi
ze. Dodatkowo zmienna step przechowywa b dzie rozmiar dopuszczalnego kroku pomi dzy kolejnymi rozmiarami punktu.
Specyfikacja biblioteki OpenGL wymaga tylko, eby obsługiwany był jeden rozmiar punku — 1.0. Na przykład
implementacja biblioteki przygotowana przez Microsoft pozwala na stosowanie punktów o wielko ci od 0.5 do 10.0 z
minimalnym krokiem 0.125. Podanie rozmiaru spoza dopuszczalnego zakresu nie jest traktowane jako bł d, po prostu
stosowany b dzie rozmiar najbli szy podanemu.
W przeciwie stwie do obiektów geometrycznych punkty nie podlegaj wpływowi perspektywy. Oznacza to, e nie staj si
one mniejsze wraz z oddalaniem si od widza i nie powi kszaj si wraz z przybli aniem si . Poza tym punkty zawsze maj
kształt kwadratu, nawet je eli za pomoc funkcji gl PointSi ze powi kszymy ich rozmiar. Na ekranie zobaczymy tylko coraz
wi ksze kwadraty! Aby uzyska punkty okr głe, musimy rysowa je z wł czonym efektem antyaliasingu. Przyjrzyjmy si
przykładowi wykorzystuj cemu te nowe funkcje. Kod z poni szego listingu tworzy tak sam spiral co kod z pierwszego
przykładu, ale tym razem punkty s stopniowo powi kszane od najmniejszej mo liwej warto ci, do najwi kszej. Przykład
pochodzi z programu POINTSZ który mo na pobra wraz z tym tutorialem.
// Definiujemy stał o warto ci liczby PI
#define GL_PI 3.1415f
// Wielko obrotów
static GLfloat xRot = 0.0f;
static GLfloat yRot = 0.0f;
// Wywoływana w celu przerysowania sceny
void RenderScene(void)
{
GLfloat x,y,z,angle;
// Zmienne przechowuj ce współrz dne i k ty
GLfloat sizes[2];
// Do zapisania dopuszczalnego zakresu wielko ci
GLfloat step;
// Zapami tuje obsługiwany przyrost rozmiaru
GLfloat curSize;
// Zapami tuje aktualny rozmiar
20
// Wyczyszczenie okna aktualnym kolorem czyszcz cym
glClear(GL_COLOR_BUFFER_BIT);
// Zapisanie stanu macierzy i wykonanie obrotu
glPushMatrix();
glRotatef(xRot, 1.0f, 0.0f, 0.0f);
glRotatef(yRot, 0.0f, 1.0f, 0.0f);
// Pobranie dopuszczalnych rozmiarów punktów
glGetFloatv(GL_POINT_SIZE_RANGE,sizes);
glGetFloatv(GL_POINT_SIZE_GRANULARITY,&step);
// Ustalenie pocz tkowego rozmiaru punktu
curSize = sizes[0];
// Ustalenie pocz tkowej współrz dnej z
z = -50.0f;
// Obracamy si w kółko trzy razy
for(angle = 0.0f; angle <= (2.0f*3.1415f)*3.0f; angle += 0.1f)
{
// Wyliczenie warto ci x i y na kole
x = 50.0f*sin(angle);
y = 50.0f*cos(angle);
// Zdefiniowanie rozmiaru punktu przed utworzeniem obiektu podstawowego
glPointSize(curSize);
// Rysowanie punktu
glBegin(GL_POINTS);
glVertex3f(x, y, z);
glEnd();
// Powi kszenie współrz dnej z i rozmiaru punktu
z += 0.5f;
curSize += step;
}
// Odtworzenie macierzy przekształce
glPopMatrix();
// Wykonanie polece rysowania
glutSwapBuffers();
}
// Ta funkcja wykonuje wszystkie konieczne inicjalizacje kontekstu renderowania
void SetupRC()
{
// Czarne tło
glClearColor(0.0f, 0.0f, 0.0f, 1.0f );
// B dziemy rysowa kolorem zielonym
glColor3f(0.0f, 1.0f, 0.0f);
}
void SpecialKeys(int key, int x, int y)
{
if(key == GLUT_KEY_UP)
xRot-= 5.0f;
if(key == GLUT_KEY_DOWN)
xRot += 5.0f;
if(key == GLUT_KEY_LEFT)
yRot -= 5.0f;
if(key == GLUT_KEY_RIGHT)
yRot += 5.0f;
if(key > 356.0f)
xRot = 0.0f;
if(key < -1.0f)
xRot = 355.0f;
if(key > 356.0f)
yRot = 0.0f;
if(key < -1.0f)
yRot = 355.0f;
// Od wie enie zawarto ci okna
glutPostRedisplay();
}
21
void ChangeSize(int w, int h)
{
GLfloat nRange = 100.0f;
// Zabezpieczenie przed dzieleniem przez zero
if(h == 0)
h = 1;
// Ustalenie wymiarów widoku na zgodnych z wymiarami okna
glViewport(0, 0, w, h);
// Ponowne ustawienie stosu macierzy rzutowania
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
// Utworzenie przestrzeni ograniczaj cej (lewo, prawo, dół, góra, blisko, daleko)
if (w <= h)
glOrtho (-nRange, nRange, -nRange*h/w, nRange*h/w, -nRange, nRange);
else
glOrtho (-nRange*w/h, nRange*w/h, -nRange, nRange, -nRange, nRange);
// Ponowne ustawienie stosu macierzy rzutowania
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
int main(int argc, char* argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
glutCreateWindow("Points Size Example");
glutReshapeFunc(ChangeSize);
glutSpecialFunc(SpecialKeys);
glutDisplayFunc(RenderScene);
SetupRC();
glutMainLoop();
return 0;
}
W powy szym przykładzie zademonstrowanych zostało kilka wa nych rzeczy. Przede wszystkim trzeba zauwa y , e funkcja
glPointSize musi by wywoływana poza zakresem obj tym funkcjami glBegin i glEnd. Nie wszystkie funkcje OpenGL mog
by wywoływane pomi dzy wywołaniami tych dwóch funkcji. Mimo e funkcja gl PointSize wpływa na wszystkie narysowane
pó niej punkty, to samo rysowanie nie mo e rozpocz si przed wywołaniem funkcji glBegin(GL_POINTS).
Je eli podamy w parametrze funkcji glPointSize rozmiar punktu wi kszy ni warto zwrócona do zmiennej size, to
zauwa ymy, e biblioteka stosowa b dzie najwi kszy dost pny rozmiar punktu, ale nie b dzie ich bardziej powi ksza .
Jest to zasada wspólna dla wszystkich funkcji OpenGL przyjmuj cych parametry z pewnego zakresu warto ci. Wszystkie
warto ci spoza dopuszczalnego zakresu modyfikowane s w taki sposób, aby si w tym zakresie zmie ciły. Warto ci zbyt
małe powi kszane s do najmniejszej dopuszczalnej warto ci, a warto ci zbyt du e zmniejszane do najwi kszej
dopuszczalnej.
Ja mo na zauwa y w przykładowym programie POINTSZ, punkty o wi kszych rozmiarach rysowane s po prostu jako
wi ksze kwadraty. Jest to domy lne zachowanie biblioteki, cho w wielu aplikacjach takie działanie jest raczej niepo dane.
Poza tym mo na si zastanawia , dlaczego mo liwe jest definiowanie punktów o wielko ci mniejszej ni jeden. Je eli punkt o
wielko ci 1.0 oznacza jeden piksel, to jak mo na przedstawia piksel o wielko ci, powiedzmy, 2.5?
Odpowied na to pytanie brzmi: parametr pobierany przez funkcj gl PointSize nie oznacza dokładnego rozmiaru punktu
w pikselach, ale przybli on rednic koła zawieraj cego wszystkie piksele u yte do narysowania punktu. Poprzez wł czenie
wygładzania punktów (ang. point smoothing) mo na zmusi bibliotek OpenGL do rysowania ładniej wygl daj cych
punktów (czyli niewielkich, wypełnionych kół). Funkcja wygładzania punktów razem z funkcj wygładzania linii (ang.
line smoothing) nale do rodziny technik o wspólnej nazwie antyaliasingu. Antyaliasing to techniki stosowane w celu wy-
gładzenia schodkowatych kraw dzi i zaokr glania rogów.
22
Rysowanie linii w trzech wymiarach
Obiekt podstawowy GL_POINTS, jakiego u ywali my do tej pory, miał bardzo proste działanie — dla ka dego zdefiniowanego
wierzchołka rysowany jest jeden punkt. Nast pnym logicznym krokiem byłoby zdefiniowanie dwóch wierzchołków i
narysowanie pomi dzy nimi linii. Dokładnie tak funkcj spełnia obiekt podstawowy GL_LINES. Poni szy wycinek kodu rysuje
linie od punktu ( 0 , 0, 0) do punktu (50, 50, 50):
glBegin(GL_LINES);
glVertex3f(0.0f, 0. 0 f , 0. 0 f ) ;
glVertex3f(50.0f, 50.0f, 50.0 f ) ;
glEnd();
Teraz jeden obiekt podstawowy definiowany jest dwoma wierzchołkami. Mi dzy ka dymi dwoma podanymi wierzchołkami
rysowana jest jedna linia. Je eli zdefiniujemy nieparzyst liczb wierzchołków, to ostatni z nich zostanie po prostu
zignorowany. Na poni szym listingu przedstawiony został nieco bardziej rozbudowany wycinek kodu rysuj cy linie roz-
chodz ce si promieni cie z punktu centralnego. Kod ten pochodzi z przykładowego programu LINES który mo na pobra
wraz z tym tutorialem. Ka dy z punktów definiowanych w tym przykładzie ma swój odpowiednik znajduj cy si po drugiej
stronie koła.
// Wywoła tylko raz, przed rysowaniem wszystkich punktów
glBegin(GL_LINES);
z = 0.0f;
for(angle = 0.0f; angle <= GL_PI; angle += (GL_PI / 20.0f))
{
// Górna połowa koła
x = 50.0f*sin(angle);
y = 50.0f*cos(angle);
glVertex3f(x, y, z);
// Dolna połowa koła
x = 50.0f*sin(angle+GL_PI);
y = 50.0f*cos(angle+GL_PI);
glVertex3f(x, y, z);
}
// Zako czenie rysowania punktów
glEnd();
Linie łamane i zamkni te
Kolejne dwa obiekty podstawowe biblioteki OpenGL opieraj si na obiektach GL_LINES, pozwalaj c zadeklarowa list
wierzchołków, na podstawie których rysowana jest linia.
Podaj c rodzaj obiektu GL_LINE_STRIP linie rysowane s od jednego wierzchołka do nast pnego, tworz c w ten sposób ci gł lini
łaman . Poni szy kod rysuje na płaszczy nie xy dwie linie wyznaczone przez trzy wierzchołki, co zostało przedstawione na rysunku
3.7.
glBegin(GL_LINE_STRIP);
glVertex3f(0.0f, O.Of, O.Of);
//V0
g!Vertex3f(50.0f. 50.Of. O.Of);
//Vl
glVertex3f(50.0f. 100.Of. O.Of);
//V2
glEnd();
23
Ostatnim liniowym obiektem podstawowym jest GL_LINE_LOOP. Zachowuje si on dokładnie tak jak GL_LINE_STRIP, jednak
ko cowa linia rysowana jest pomi dzy ostatnim a pierwszym podanym wierzchołkiem. W ten sposób mo na bardzo łatwo
rysowa figury zamkni te. Na rysunku 3.8 przedstawiono obiekt GL_LINE_LOOP narysowany w oparciu o te same wierzchołki
co obiekt GL_LINF_STRIP z rysunku 3.7.
Aproksymacja krzywych liniami prostymi
Przykładowy program POINTS pokazał nam, jak mo na za pomoc rysowania punktów utworzy co w rodzaju spirali.
Kusz c mo e wydawa si propozycja zag szczenia punktów (poprzez zmniejszenie warto ci inkre-mcntacji k ta), aby w
ten sposób uzyska spiraln krzyw , a nie zbiór punktów przybli aj cych ten kształt. Jest to co prawda działanie całkowicie
prawidłowe, ale w przypadku bardziej skomplikowanych krzywych, składaj cych si z tysi cy punktów, mo e działa
bardzo wolno.
Lepszym sposobem aproksymacji krzywej jest stosowanie obiektów GL_LINE_STRIP i zabawa w „poł cz punkty". Wraz z
zag szczaniem punktów tworzona jest dokładniejsza krzywa, w której nie musimy definiowa wszystkich jej punktów. Jak
wida , aproksymacja krzywej wygl da bardzo dobrze. Jak b dzie mo na si przekona , ta technika jest wszechobecna w
programach wykorzystuj cych bibliotek OpenGL.
// Wywoła tylko raz, przed rysowaniem wszystkich punktów
glBegin(GL_LINE_STRIP);
z = -50.0f;
for(angle = 0.0f; angle <= (2.0f*3.1415f)*3.0f; angle += 0.1f)
{
x = 50.0f*sin(angle);
y = 50.0f*cos(angle);
// Okre lenie punktu i przesuni cie współrz dnej Z
glVertex3f(x, y, z);
z += 0.5f;
}
Zako czenie rysowania punktów
glEnd();
Ustalanie szeroko ci linii
Tak jak mo liwe jest ustalenie rozmiaru rysowanych punktów, w podobny sposób da si te okre la szeroko rysowanych linii za
pomoc funkcji glLineWidth:
void glLineWidth(GLfloat width);
Funkcja gl LineWidth pobiera jeden parametr okre laj cy przybli on szeroko rysowanej linii podan w pikselach.
Niestety, podobnie jak było w przypadku punktów, nie mo na poda dowolnej szeroko ci linii, dlatego nale y si
upewni , czy podawana szeroko b dzie obsłu ona prawidłowo. Mo na u y poni szego kodu w celu sprawdzenia
zakresu dost pnych szeroko ci linii i najmniejszych odst pów mi dzy nimi.
GLfloat sizes[2];
//Zapami tuje dopuszczalny zakres szeroko ci
GLfloat step;
//Zapami tuje obsługiwany przyrost szeroko ci
24
// Pobranie dopuszczalnych szeroko ci linii i wielko ci kroku
glGetFloatv(GL_LINE_WIDTH_RANGE, sizes);
glGetFloatv(GL_LINE_WIDTH_GRANULARITY, &step);
W tablicy sizes znajd si dwa elementy okre laj ce najmniejsz i najwi ksz warto przyjmowan przez funkcj gl
LineWidth. Dodatkowo, zmienna step przechowywa b dzie wielko dopuszczalnego kroku pomi dzy kolejnymi
szeroko ciami linii. Specyfikacja biblioteki OpenGL wymaga tylko, eby obsługiwana była jedna szeroko linii — 1.0.
Na przykład implementacja biblioteki przygotowana przez Microsoft pozwala na stosowanie linii o szeroko ci od 0.5 do
10.0 z minimalnym krokiem 0.125.
Poni szy listing przedstawia kod dobrego przykładu wykorzystania funkcji glLineWidth. Pochodzi on z programu
LINESW i rysuje w oknie dziesi linii o ró nych szeroko ciach. Rysowanie zaczynane jest w dolnej cz ci okna, od
warto ci -90 w osi y, a nast pnie warto ci współrz dnej y s powi kszane o 20 przy rysowaniu ka dej kolejnej linii. Dla
ka dej kolejnej rysowanej linii jej szeroko powi kszana jest o 1.
Prosz zauwa y , e tym razem do podawania współrz dnych linii u ywali my funkcji glVertex2f, a nie glVertex3f. Jak
ju wspomniano wcze niej, pozwala to na osi gni cie wi kszej wygody, poniewa i tak rysujemy na płaszczy nie xy,
gdzie warto współrz dnej z jest zawsze równa 0. Aby si przekona , e rzeczywi cie nadal rysujemy w trzech
wymiarach, wystarczy nacisn klawisze ze strzałkami i obróci rysunek w dowolnym kierunku. Zobaczymy wtedy, e
wszystkie narysowane linie le na tej samej płaszczy nie.
// Wywoływana w celu przerysowania sceny
void RenderScene(void)
{
GLfloat y;
// Przechowuje warto współrz dnej Y
GLfloat fSizes[2];
// Dane zakresu szeroko ci linii
GLfloat fCurrSize;
// Zapisanie aktualnej szeroko ci
...
...
...
// Pobranie informacji o dopuszczalnych szeroko ciach linii
// i zapisanie najmniejszej mo liwej warto ci
glGetFloatv(GL_LINE_WIDTH_RANGE,fSizes);
fCurrSize = fSizes[0];
// Współrz dn y zwi kszamy o 20 jednostek
// przy ka dym obiegu p tli
for(y = -90.0f; y < 90.0f; y += 20.0f)
{
// Ustalenie szeroko ci linii
glLineWidth(fCurrSize);
// Rysowanie linii
glBegin(GL_LINES);
glVertex2f(-80.0f, y);
glVertex2f(80.0f, y);
glEnd();
// Zwi kszenie szeroko ci linii
fCurrSize += 1.0f;
}
...
...
}
Rysowanie trójk tów w trzech wymiarach
Wiemy ju , jak mo na rysowa punkty i linie, a nawet jak za pomoc obiektu podstawowego GL_LINE_LOOP budowa
zamkni te wielok ty. Ju za pomoc tych obiektów podstawowych mo na w prosty sposób budowa dowolne kształty w trzech
wymiarach. Mo na, na przykład, narysowa sze kwadratów, układaj c je tak, aby razem tworzyły sze cian.
Niestety, jak mo na było zauwa y , wszystkie kształty tworzone w ten sposób nie s wypełniane adnym kolorem, a poza tym
rysowane s tylko linie. I rzeczywi cie, narysowany takimi metodami sze cian b dzie tylko szkieletem sze cianu, bez
wypełnienia. Aby uzyska pełne powierzchnie, potrzebne nam jest co wi cej ni tylko punkty i linie. Do takich celów
musimy zastosowa wielok ty. Wielok t (polygon) jest kształtem zamkni tym, który mo e (cho nie musi) by
wypełniany aktualnie wybranym kolorem. Takie wła ciwo ci nadaj mu status bazowego elementu wszystkich
wypełnianych kompozycji tworzonych w OpenGL.
25
Trójk t — mój pierwszy wielok t
Najprostszym z mo liwych wielok tów jest posiadaj cy tylko trzy boki trójk t. Obiekt podstawowy GL_TRIANGLES
umo liwia rysowanie trójk tów poprzez poł czenie trzech zadeklarowanych wierzchołków. Poni szy kod rysuje dwa
trójk ty, z których ka dy korzysta z trzech podanych wierzchołków, co pokazano na rysunku 3.14.
glBegin(GL_TRANGLES);
glVertex2r(0.0f. 0.0f);
//V0
glVertex2f(25.0f. 25.0f);
//V1
glVertex2f(50.0f. 0.0f):
//V2
glVertex2f(-50.0f, 0.0f);
//V3
glVcrtex2f(-75.0f,
50.0f):
//V4
glVertex2f(-25.0F, 0.0f);
//V5
glEnd();
Nawini cie
Na rysunku 3.14 przedstawiona została bardzo wa na cecha ka dego wielok tnego obiektu podstawowego. Prosz
zwróci uwag na strzałki na liniach ł cz cych poszczególne wierzchołki. Podczas rysowania pierwszego trójk ta linie
rysowane s od wierzchołka V0 do V1, potem do V2 i na ko cu znów do V0, dzi ki czemu trójk t zostaje zamkni ty.
Ta cie ka odpowiada kolejno ci definiowania wierzchołków, a z naszego punktu widzenia, s one uło one zgodnie z ruchem
wskazówek zegara. Drugi z trójk tów ma dokładnie tak sam charakterystyk kierunkow .
Kombinacja kolejno ci i kierunku w którym definiowane s wierzchołki nazywana jest nawini ciem (ang. winding). O
trójk tach z rysunku 3.14 mówi si , e posiadaj nawini cie zgodne z ruchem wskazówek zegara, poniewa układaj si
dokładnie w tym kierunku. Je eli w trójk cie znajduj cym si po lewej stronie zamienimy miejscami wierzchołki V4 i V5,
to powstały w ten sposób trójk t b dzie miał nawini cie przeciwne do ruchu wskazówek zegara. Rysunek 3.15 przedstawia te
dwa trójk ty o przeciwnych nawini ciach.
Domy lnie biblioteka OpenGL uznaje wielok ty o nawini ciu przeciwnym do kierunku ruchu wskazówek zegara za uło one
przodem. Oznacza to, e trójk t znajduj cy si na rysunku 3.15 po lewej stronie b dzie ustawiony przodem do widza,
natomiast trójk t z prawej strony b dzie ustawiony tyłem do widza.
Dlaczego jest to tak wa ne? Jak wkrótce zobaczymy, bardzo cz sto b dziemy chcieli nada inne charakterystyki przedniej
i tylnej stronie wielok ta. Tyln stron mo emy całkowicie ukry albo nada jej inny kolor i wła ciwo ci odblaskowe. Bardzo
26
wa ne jest, eby nawini cie wszystkich wielok tów na scenie było zawsze takie samo, tak aby przednie strony wielok tów znaj-
dowały si na zewn trznych powierzchniach tworzonych obiektów. W kolejnym podrozdziale (dotycz cym pełnych obiektów)
zobrazujemy t zasad , za pomoc kilku bardziej zło onych modeli.
Je eli konieczne b dzie odwrócenie tego domy lnego zachowania biblioteki OpenGL, mo na to zrobi , wywołuj c
poni sz funkcj :
lgFront(GL_CW);
Parametr GL_CW nakazuje bibliotece OpenGL, eby traktowała wielok ty o nawini ciu zgodnym z ruchem wskazówek
zegara jako ukierunkowane do przodu. Aby powróci do normalnego traktowania wielok tów o nawini ciu przeciwnym
do ruchu wskazówek zegara, nale y wywoła t funkcj z parametrem GL_CCW.
Trójk ty sklejane
Tworz c wiele powierzchni i kształtów, b dziemy musieli narysowa kilka poł czonych ze sob trójk tów. Mo na
oszcz dzi przy tym wiele czasu, rysuj c ci g sklejonych ze sob trójk tów za pomoc obiektu podstawowego
GL_TRIANGLE_STRIP. Rysunek 3.16 przedstawia post p rysowania trzech sklejonych trójk tów okre lanych przez
definicje pi ciu wierzchołków ponumerowanych od V0 do V4. Jak wida , kolejne wierzchołki nie musz by przetwarzane w
tej samej kolejno ci, w jakiej s definiowane. Wynika to z konieczno ci zachowania przeciwnego do ruchu wskazówek zegara
nawini cia ka dego trójk ta. Kolejnym trójk tom wierzchołki przydzielane s nast puj co: V0, V1, V2, potem V2, V1, V3, V2,
V3, V4 itd.
Od teraz w opisach pozostałych obiektów podstawowych nie b dziemy podawa ju wycinków kodów demonstruj cych
tworzenie wierzchołków po wywołaniu funkcji glBegm. Do tej pory wszyscy powinni ju zrozumie , o co chodzi. Pó niej,
gdy b dziemy mieli ju gotowy program przykładowy, przypomnimy sobie te wszystkie przykłady.
U ywanie trójk tów sklejanych wi e si z dwoma zaletami w porównaniu do definiowania ka dego trójk ta z osobna. Po
pierwsze, po zdefiniowaniu trzech wierzchołków pierwszego trójk ta ka dy kolejny trójk t tworzymy, definiuj c tylko jeden
wierzchołek. W ten sposób oszcz dzamy wiele miejsca przeznaczonego na przechowywanie danych. Szczególnie gdy nasz
obiekt składa si z wielu trójk tów. Drug zalet jest poprawa wydajno ci oblicze matematycznych i oszcz dno ci przy
przesyłaniu danych. Mniej wierzchołków oznacza szybsze przesłanie danych z pami ci komputera do karty graficznej, a
tak e mniej przekształce wierzchołków.
27
Wachlarze trójk tów
Oprócz mo liwo ci tworzenia trójk tów sklejanych mo emy te tworzy grupy trójk tów tworz cych wachlarz wokół
pewnego rodkowego punktu, czyli obiekt podstawowy GL_TRIANGLE_FAN. Rysunek 3.17 przedstawia taki wachlarz
składaj cy si z trzech trójk tów definiowanych przez cztery wierzchołki. Pierwszy wierzchołek (V
0
) wyznacza rodek
tego wachlarza. Pierwsze trzy zdefiniowane wierzchołki u ywane s do narysowania pierwszego trójk ta, jednak ka dy
kolejny wierzchołek w poł czeniu z wierzchołkiem centralnym i poprzednio zdefiniowanym (V
n
- 1) definiuje nowy
trójk t.
Budowanie pełnych obiektów
Tworzenie z trójk tów (lub innych wielok tów) pełnych, zamkni tych obiektów to zadanie wymagaj ce czego wi cej ni tylko
poł czenia ze sob serii wierzchołków zdefiniowanych w trzech wymiarach. Przyjrzyjmy si przykładowemu programowi
TRIANGLE, w którym do zbudowania w przestrzeni widocznej sto ka wykorzystano dwa wachlarze trójk tów. Pierwszy
wachlarz tworzy kształt sto ka, przy czym pierwszy wierzchołek wyznacza czubek sto ka, a pozostałe wierzchołki tworz
koło wokół osi z. Drugi wachlarz definiuje koło całkowicie le ce na płaszczy nie xy.
Patrz c na sto ek wzdłu osi z, zobaczymy tylko koło utworzone z wachlarza trójk tów. Poszczególne trójk ty uwypuklane
s poprzez stosowanie zamiennie kolorów czerwonego i zielonego.
Kod funkcji SetupRC i RenderScene przedstawiony został na listingu poni ej. Znajduje si w nim kilka nieopisywanych do tej pory
zmiennych i stałych, które s w nim krótko obja niane. Program jest demonstracj kilku aspektów budowania obiektów
trójwymiarowych. Klik-ni cie w oknie prawym klawiszem myszy wywołuje menu kontekstowe obsługuj ce ró ne efekty. Jest
ono wykorzystywane do wł czania i wył czania pewnych efektów rysowania w trzech wymiarach, umo liwiaj c nam
zapoznanie si z niektórymi charakterystykami tworzenia obiektów trójwymiarowych.
// Funkcja wykonuje wszystkie konieczne inicjalizacje kontekstu renderowania
void SetupRC()
{
// Czarne tło
glClearColor(0.0f, 0.0f, 0.0f, 1.0f );
// Kolor rysuj cy - zielony
glColor3f(0.0f, 1.0f, 0.0f);
// Model cieniowania kolorów - płaski
glShadeModel(GL_FLAT);
// Wielok ty o nawini ciu zgodnym z ruchem wskazówek zegara traktowane s
// jako skierowane do przodu. Takie ustawienie jest konieczne, poniewa
// korzystamy z wachlarzy trójk tów.
glFrontFace(GL_CW);
}
// Wywoływana w celu przerysowania sceny
void RenderScene(void)
{
GLfloat x,y,angle; // Przechowuj warto ci współrz dnych i k ta
int iPivot = 1;
// Do oznaczania zamiennych kolorów
// Wyczyszczenie okna i bufora gł bi
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Wł czenie lub wył czenie mechanizmu eliminowania ukrytych powierzchni
28
if(iCull)
glEnable(GL_CULL_FACE);
else
glDisable(GL_CULL_FACE);
// Wł czenie lub wył czenie mechanizmu sprawdzania gł bi
if(iDepth)
glEnable(GL_DEPTH_TEST);
else
glDisable(GL_DEPTH_TEST);
// Je eli ten znacznik b dzie ustawiony, to wielok ty zwrócone
// tyłem do widza b d rysowane tylko w postaci szkieletu
if(iOutline)
glPolygonMode(GL_BACK,GL_LINE);
else
glPolygonMode(GL_BACK,GL_FILL);
// Zapisanie stanu macierzy i wykonanie obrotu
glPushMatrix();
glRotatef(xRot, 1.0f, 0.0f, 0.0f);
glRotatef(yRot, 0.0f, 1.0f, 0.0f);
// Rozpocz cie rysowania wachlarza trójk tów
glBegin(GL_TRIANGLE_FAN);
// Czubek sto ka jest wspólnym wierzchołkiem wszystkich trójk tów z wachlarza
// wysuni tym do góry w osi z. W ten sposób zamiast koła powstanie sto ek.
glVertex3f(0.0f, 0.0f, 75.0f);
// Wykonujemy obrót w około i oznaczamy w równych odst pach wierzchołki
// tworz ce wachlarz trójk tów.
for(angle = 0.0f; angle < (2.0f*GL_PI); angle += (GL_PI/8.0f))
{
// Wyliczenie współrz dnych x i y kolejnego wierzchołka
x = 50.0f*sin(angle);
y = 50.0f*cos(angle);
// Wybieranie koloru - zielony lub czerwony
if((iPivot %2) == 0)
glColor3f(0.0f, 1.0f, 0.0f);
else
glColor3f(1.0f, 0.0f, 0.0f);
// Inkrementacja zmiennej okre laj cej rodzaj koloru
iPivot++;
// Definiowanie kolejnego wierzchołka w wachlarzu trójk tów
glVertex2f(x, y);
}
// Zako czenie rysowania trójk tów sto ka
glEnd();
// Rozpocz cie rysowania kolejnego wachlarza trójk tów
// zakrywaj cego podstaw sto ka
glBegin(GL_TRIANGLE_FAN);
// rodek wachlarza znajduje si na pocz tku układu współrz dnych
glVertex2f(0.0f, 0.0f);
for(angle = 0.0f; angle < (2.0f*GL_PI); angle += (GL_PI/8.0f))
{
// Wyliczenie współrz dnych x i y kolejnego wierzchołka
x = 50.0f*sin(angle);
y = 50.0f*cos(angle);
// Wybieranie koloru - zielony lub czerwony
if((iPivot %2) == 0)
glColor3f(0.0f, 1.0f, 0.0f);
else
glColor3f(1.0f, 0.0f, 0.0f);
// Inkrementacja zmiennej okre laj cej rodzaj koloru
iPivot++;
// Definiowanie kolejnego wierzchołka w wachlarzu trójk tów
glVertex2f(x, y);
}
// Zako czenie rysowania trójk tów podstawy sto ka
glEnd();
// Odtworzenie macierzy przekształce
29
glPopMatrix();
// Wysłanie polece do wykonania
glutSwapBuffers();
}
Ustalanie koloru wielok ta
Do tej pory kolor ustalali my tylko raz i rysowali my tylko pojedynczy kształt. Teraz jednak, przy rysowaniu wielu
wielok tów, zrobiło si naprawd interesuj co. Stosujemy teraz ró ne kolory, aby mo liwe było ogl danie efektów naszej
pracy. Tak naprawd to kolory przydziela si poszczególnym wierzchołkom, a nie całym wielok tom. Model cieniowania
okre la, czy wielok t b dzie pokryty jednolitym kolorem (kolor pobierany jest z ostatniego wierzchołka tworz cego
wielok t) czy mo e pokryty płynnym przej ciem pomi dzy kolorami poszczególnych wierzchołków.
W linii:
glShadeModel(GL_FLAT);
okre lamy, e biblioteka OpenGL b dzie wypełniała wielok ty jednolitym kolorem, pobranym z ostatnio zdefiniowanego
wierzchołka tego wielok ta. W ten sposób mo emy po prostu zmienia aktualny kolor na zielony lub czerwony tu przed
zdefiniowaniem kolejnego wierzchołka wachlarza trójk tów. Z drugiej strony linia:
glShadeModel(GL_SMOOTH);
nakazałaby bibliotece miesza ze sob kolory definiowane w aktualnym wielok cie przez wszystkie jego wierzchołki.
Tekstury
Ładowanie tekstur
Pierwszym krokiem w procedurze nakładania tekstur na obiekty geometryczne jest załadowanie tekstury do pami ci. Po
załadowaniu tekstura staje si cz ci aktualnego stanu tekstur (ang. texture state) — wi cej na ten temat w dalszej cz ci
rozdziału. Do ładowania tekstur z bufora w pami ci (gdzie znalazły si po wczytaniu z pliku) najcz ciej stosowane s trzy
funkcje:
void glTexImage1D(GLenum target, GLint level, GLenum internalFormat,
GLsizei width, GLint border,
GLenum format, GLenum type. void *data);
void glTexImage2D(GLenum target, GLint level, GLenum internalFormat,
GLsizei width, GLsizei height, GLint border,
GLenum format, GLenum type. void *data);
void glTexImage3D(GLenum target, GLint level, GLenum internalFormat,
GLsizei width, GLsizei height, GLsizei depth, GLint border,
GLenum format, GLenum type. void *data);
Te trzy przydługie funkcje przekazuj bibliotece OpenGL wszystkie konieczne informacje o sposobach interpretowania informacji o
danych tekstury wskazywanej przez parametr data.
Pierwsza rzecz, jaka rzuca si w oczy, to fakt, e wszystkie trzy funkcje s tylko wariantami podstawowej funkcji g1TexImage.
Biblioteka OpenGL obsługuje tekstury jedno-, dwu- i trójwymiarowe, a te trzy funkcje umo liwiaj załadowanie ka dego z tych
rodzajów tekstur i ustawienie wczytanej tekstury jako aktualnej. Trzeba by wiadomym tego, e po wywołaniu jednej z tych funkcji,
biblioteka OpenGL kopiuje dane tekstury wskazywane parametrem data.
W parametrze target wszystkich trzech funkcji nale y podawa warto ci GL_TEXTURE_1D, GL_TEXTURE_2D lub
GL_TEXTURE_3D odpowiednio dla rodzaju funkcji. Podaj c warto ci GL_PROXY_TEXTURE_1D, GL_PROXY_TEXTURE_2D
lub GL_PROXY_TEXTURE_3D, mo na te definiowa tekstury zast pcze (proxy). Funkcja glGetTexParameter pozwoli nam wtedy
sprawdzi wyniki prób utworzenia takiej zast pczej tekstury.
Parametr level okre la poziom ładowanej mipmapy. Je eli nie u ywamy mipmap b dziemy stosowa tutaj warto 0.
Nast pnie musimy poda warto parametru internalFormat opisuj cego dane tekstury. W ten sposób informujemy
bibliotek OpenGL, ile składowych kolorów ma opisywa ka dy teksel, podajemy równie rozmiar poszczególnych
składowych i ewentualn informacj o kompresji tekstury. W tabeli 8.1 podajemy najcz ciej stosowane warto ci tego
30
parametru.
Parametry width, height i depth okre laj wymiary ładowanej tekstury. Trzeba zaznaczy , e te wymiary musz by
całkowitymi pot gami liczby 2 (1, 2, 4, 8, 16, 32, 64 itd.). Nie istnieje wymóg, eby tekstury były kwadratowe (wszystkie
wymiary musiałyby mie identyczn wielko ), jednak załadowanie tekstury o wielko ci nie b d cej pot g liczby 2 spowoduje
natychmiastowe wył czenie mechanizmu teksturowania.
Parametr border umo liwia okre lenie szeroko ci obramowania tekstury. Umo liwia to powi kszenie szeroko ci,
wysoko ci i gł boko ci tekstury o dodatkowe piksele wstawiane na jej brzegach. Graj one bardzo wa n rol w opisanym
nieco dalej mechanizmie filtrowania tekstur, jednak na razie b dziemy temu parametrowi nadawa warto 0.
Ostatnie trzy parametry — format, type i data — maj takie samo znaczenie jak w funkcji g!0rawPixels u ywanej do
ładowania danych obrazów do bufora kolorów. Dla wygody prawidłowe warto ci stałych dla parametrów format i type
podajemy ponownie w tabelach 8.2 i 8.3.
Je eli nie zostanie wł czony odpowiedni stan tekstur, to załadowane tekstury nie zostan nało one na obiekty geometryczne.
Wł czanie i wył czanie teksturowania dla poszczególnych stanów wykonujemy, wywołuj c funkcje giEnabie i glDisable
z parametrami GL_TEXTURE_1D, GL_TEXTURE_2D lub GL_TEXTURE_3D. Dla jednej jednostki teksturuj cej mo e by wł czony
tylko jeden spo ród tych trzech stanów teksturowania. Poza tym dobr praktyk jest wył czanie nieu ywanych stanów
teksturowania. Na przykład, aby przeł czy si pomi dzy teksturowaniem jednowymiarowym na teksturowanie
dwuwymiarowe, nale y wywoła poni sze funkcje:
glDisable(GL_TEXTURE_1D);
glEnable(GL TEXTURE_2D);
31
32
Odwzorowywanie tekstur na obiekty geometryczne
Załadowanie tekstury i wł czenie mechanizmu teksturowania powoduje, e tekstura nakładana jest na ka dy obiekt
podstawowy. Musimy jednak przekaza bibliotece informacje o tym, jak ten proces ma by wykonywany. Wykonywane
jest to poprzez przypisanie ka demu wierzchołkowi współrz dnych tekstury. Poszczególne teksele wewn trz tekstury
nie s adresowane jak komórki pami ci (tak jak jest to w piksmapach), ale za pomoc bardziej abstrakcyjnych
współrz dnych tekstury. Współrz dne tekstury najcz ciej definiowane s jako warto ci zmiennoprzecinkowe
ograniczone do zakresu od 0.0 do 1.0. Współrz dne tekstury oznaczane s literami s, r, t i q (odpowiadaj ce
współrz dnym wierzchołków x, y, z i w) opisuj cymi jedno, dwu i trójwymiarowe tekstury, a tak e współrz dn ich
skalowania.
Na rysunku 8.2 przedstawiono jedno-, dwu- i trójwymiarow tekstur oraz sposób, w jaki współrz dne opisuj teksele.
Nie istniej tekstury czterowymiarowe, nasuwa si wi c pytanie, co ma oznacza współrz dna c/? Jest ona
odpowiednikiem geometrycznej współrz dnej w, czyli współczynnikiem skalowania stosowanym wobec pozostałych
współrz dnych tekstury. Oznacza to, e wszystkie współrz dne opisuj ce tekstury maj rzeczywiste warto ci s/g, t/g i r/q.
Domy lnie współrz dnej a nadawana jest warto 1.0.
Współrz dne tekstury wyznaczane s za pomoc funkcj i glTexCoord. Podobnie jak funkcje definiuj ce współrz dne
wierzchołków, wektorów normalnych lub warto ci kolorów ta funkcja równie dost pna jest w wielu podobnych wersjach.
Poni sze trzy wersje funkcji b d u ywane w przykładowych programach:
void glTexCoord1f(GLfloat s ) ;
void glT exCoord2f(GLfloat s. Glfloat t ) ;
v o i d g l T e xC o o r d 3 f ( G L f l o a t s . G l f l o a t t , G l f l o a t r ) ;
Za pomoc tych funkcji mo na przypisa ka demu wierzchołkowi współrz dne tekstury. Nast pnie, w czasie nakładania
tekstury na obiekt geometryczny, biblioteka OpenGL rozci ga lub ciska tekstur tak, eby dopasowa j do odpowiednich
współrz dnych. Rozci ganie i ciskanie tekstury odbywa si poprzez aktualny/////- tekstury, który równie b dziemy
pokrótce opisywa . Na rysunku 8.3 przedstawiono przykładowe odwzorowanie dwuwymiarowej tekstury na obiekt
GL_QUAD. Prosz zauwa y , e rogi tekstury powi zane s z rogami czworok ta. Współrz dne tekstury musz by
definiowane przed wierzchołkiem, podobnie jak inne wła ciwo ci wierzchołków (materiały, wektory normalne itd.)!
Niestety, takie doskonałe dopasowanie tekstury do obiektu geometrycznego wyst puje niezwykle rzadko. W celu lepszego
wyja nienia znaczenia współrz dnych tekstury na rysunku 8.4 przedstawiamy kolejny przykład ich zastosowania. Na tym
rysunku widoczna jest równie kwadratowa tekstura, ale nakładana jest ona na inny obiekt geometryczny — trójk t.
Współrz dne tekstury nało one na tekstur odpowiadaj miejscom na teksturze przypisanym poszczególnym
wierzchołkom trójk ta.
33
Macierze tekstur
Współrz dne tekstury mog by równie przekształcane za pomoc macierzy tekstur. Stos macierzy tekstur działa
tak samo jak stosy innych rodzajów opisywanych wcze niej macierzy (model-widok, rzutowania i kolorów). Podaj c
funkcji glMatnxMode parametr GL TEXTURE, powodujemy, e wszystkie kolejne wywołania funkcji operuj cych na ma-
cierzach b d działały na macierzach tekstur:
glMatrixMode(GL_TEXTURE);
Stos macierzy tekstur mo e mie zaledwie dwa poziomy gł boko ci, z których mog korzysta funkcje glPushMatrix i
glPopMatrix. Współrz dne tekstur mog by te przesuwane skalowane, a nawet obracane.
Prosty przykład dwuwymiarowy
Załadowanie tekstury i przypisanie jej współrz dnych to podstawowe operacje wykonywane w czasie odwzorowywana
tekstur. Musimy si jednak zaj jeszcze kilkoma innymi sprawami, takimi jak zawijanie współrz dnych, filtry tekstur i
rodowisko tekstur. Co znacz te poj cia i jak nale y z nich korzysta ? Zwolnijmy mo e na chwil i przyjrzyjmy si
przykładowi wykorzystuj cemu tekstur dwuwymiarow . W poni szym kodzie wykorzystywane s funkcje, które zostały ju
opisane, ale równie kilka zupełnie nowych. Cały ten przykład ma nam słu y jako podstawa do opisywania pozostałych
problemów zwi zanych z odwzorowywaniem tekstur.
Poni szy listing zawiera cało kodu przykładowego programu PYRAMID. Program rysuje prost , cztero cienn piramid
zbudowan z trójk tów. Na ka dy z boków, jak równie na podstaw , nakładana jest tekstura z obrazem kamienia.
Podobnie jak w poprzednich przykładach mo emy obraca piramid , naciskaj c klawisze ze strzałkami.
34
#include "../../Common/OpenGLSB.h"
// Biblioteki systemowe i OpenGL
#include "../../Common/GLTools.h" // Biblioteka GLTools
// Wielko ci obrotów
static GLfloat xRot = 0.0f;
static GLfloat yRot = 0.0f;
// Zmiana przestrzeni widocznej i okna.
// Wywoływana w momencie zmiany rozmiaru okna
void ChangeSize(int w, int h)
{
GLfloat fAspect;
// Zabezpieczenie przed dzieleniem przez zero
if(h == 0)
h = 1;
// Zrównanie wielko ci widoku i okna
glViewport(0, 0, w, h);
fAspect = (GLfloat)w/(GLfloat)h;
// Ustalenie układu współrz dnych
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
// Utworzenie rzutowania perspektywicznego
gluPerspective(35.0f, fAspect, 1.0, 40.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
// Ta funkcja wykonuje wszystkie konieczne inicjalizacje kontekstu renderowania.
// Tutaj, konfiguruje i inicjalizuje o wietlenie sceny
void SetupRC()
{
GLubyte *pBytes;
GLint iWidth, iHeight, iComponents;
GLenum eFormat;
// Warto ci i współrz dne wiatła
GLfloat whiteLight[] = { 0.05f, 0.05f, 0.05f, 1.0f };
GLfloat sourceLight[] = { 0.25f, 0.25f, 0.25f, 1.0f };
GLfloat lightPos[] = { -10.f, 5.0f, 5.0f, 1.0f };
glEnable(GL_DEPTH_TEST); // Usuwanie ukrytych powierzchni
glEnable(GL_CULL_FACE); // Nie b dziemy prowadzi oblicze wn trza samolotu
glFrontFace(GL_CCW); // Wielok ty z nawini ciem przeciwnym do ruchu wskazówek zegara
// Wł czenie o wietlenia
glEnable(GL_LIGHTING);
// Konfiguracja i wł czenie wiatła numer 0
glLightModelfv(GL_LIGHT_MODEL_AMBIENT,whiteLight);
glLightfv(GL_LIGHT0,GL_AMBIENT,sourceLight);
glLightfv(GL_LIGHT0,GL_DIFFUSE,sourceLight);
glLightfv(GL_LIGHT0,GL_POSITION,lightPos);
glEnable(GL_LIGHT0);
// Wł czenie ledzenia kolorów
glEnable(GL_COLOR_MATERIAL);
// Wła ciwo ci o wietlenia otoczenia i rozproszenia
// b d ledzi warto ci podawane funkcji glColor
glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
// Czarne tło
glClearColor(0.0f, 0.0f, 0.0f, 1.0f );
// Ładowanie tekstury
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
pBytes = gltLoadTGA("Stone.tga", &iWidth, &iHeight, &iComponents, &eFormat);
glTexImage2D(GL_TEXTURE_2D, 0, iComponents, iWidth, iHeight, 0, eFormat, GL_UNSIGNED_BYTE, pBytes);
free(pBytes);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
35
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glEnable(GL_TEXTURE_2D);
}
// Reakcje na klawisze strzałek
void SpecialKeys(int key, int x, int y)
{
if(key == GLUT_KEY_UP)
xRot-= 5.0f;
if(key == GLUT_KEY_DOWN)
xRot += 5.0f;
if(key == GLUT_KEY_LEFT)
yRot -= 5.0f;
if(key == GLUT_KEY_RIGHT)
yRot += 5.0f;
xRot = (GLfloat)((const int)xRot % 360);
yRot = (GLfloat)((const int)yRot % 360);
// Od wie enie zawarto ci okna
glutPostRedisplay();
}
// Wywoływana w celu przerysowania sceny
void RenderScene(void)
{
GLTVector3 vNormal;
GLTVector3 vCorners[5] = { { 0.0f, .80f, 0.0f }, // Góra 0
{ -0.5f, 0.0f, -.50f }, // Lewy tył 1
{ 0.5f, 0.0f, -0.50f }, // Prawy tył 2
{ 0.5f, 0.0f, 0.5f }, // Prawy przód 3
{ -0.5f, 0.0f, 0.5f }}; // Lewy przód 4
// Czyszczenie okna aktualnym kolorem czyszcz cym
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Zapisanie stanu macierzy i wykonanie obrotów
glPushMatrix();
// Cofni cie obiektów
glTranslatef(0.0f, -0.25f, -4.0f);
glRotatef(xRot, 1.0f, 0.0f, 0.0f);
glRotatef(yRot, 0.0f, 1.0f, 0.0f);
// Rysowanie piramidy
glColor3f(1.0f, 1.0f, 1.0f);
glBegin(GL_TRIANGLES);
// Podstawa piramidy - dwa trójk ty
glNormal3f(0.0f, -1.0f, 0.0f);
glTexCoord2f(1.0f, 1.0f);
glVertex3fv(vCorners[2]);
glTexCoord2f(0.0f, 0.0f);
glVertex3fv(vCorners[4]);
glTexCoord2f(0.0f, 1.0f);
glVertex3fv(vCorners[1]);
glTexCoord2f(1.0f, 1.0f);
glVertex3fv(vCorners[2]);
glTexCoord2f(1.0f, 0.0f);
glVertex3fv(vCorners[3]);
glTexCoord2f(0.0f, 0.0f);
glVertex3fv(vCorners[4]);
// Przednia strona
gltGetNormalVector(vCorners[0], vCorners[4], vCorners[3], vNormal);
glNormal3fv(vNormal);
glTexCoord2f(0.5f, 1.0f);
glVertex3fv(vCorners[0]);
glTexCoord2f(0.0f, 0.0f);
glVertex3fv(vCorners[4]);
glTexCoord2f(1.0f, 0.0f);
glVertex3fv(vCorners[3]);
// Lewa strona
gltGetNormalVector(vCorners[0], vCorners[1], vCorners[4], vNormal);
glNormal3fv(vNormal);
glTexCoord2f(0.5f, 1.0f);
glVertex3fv(vCorners[0]);
glTexCoord2f(0.0f, 0.0f);
36
glVertex3fv(vCorners[1]);
glTexCoord2f(1.0f, 0.0f);
glVertex3fv(vCorners[4]);
// Tylna strona
gltGetNormalVector(vCorners[0], vCorners[2], vCorners[1], vNormal);
glNormal3fv(vNormal);
glTexCoord2f(0.5f, 1.0f);
glVertex3fv(vCorners[0]);
glTexCoord2f(0.0f, 0.0f);
glVertex3fv(vCorners[2]);
glTexCoord2f(1.0f, 0.0f);
glVertex3fv(vCorners[1]);
// Prawa strona
gltGetNormalVector(vCorners[0], vCorners[3], vCorners[2], vNormal);
glNormal3fv(vNormal);
glTexCoord2f(0.5f, 1.0f);
glVertex3fv(vCorners[0]);
glTexCoord2f(0.0f, 0.0f);
glVertex3fv(vCorners[3]);
glTexCoord2f(1.0f, 0.0f);
glVertex3fv(vCorners[2]);
glEnd();
// Odtworzenie stanu macierzy
glPopMatrix();
// Zamiana buforów
glutSwapBuffers();
}
int main(int argc, char *argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
glutInitWindowSize(800, 600);
glutCreateWindow("Textured Pyramid");
glutReshapeFunc(ChangeSize);
glutSpecialFunc(SpecialKeys);
glutDisplayFunc(RenderScene);
SetupRC();
glutMainLoop();
return 0;
}
W funkcji SetupRC wykonywane s niezb dne inicjalizacje programu, w tym równie załadowanie tekstury za pomoc
funkcji gltLoadTGA, która to jest doł czona do tego tutoriala i umo liwia ona szybkie załadowanie tekstury z pliku .tga.
Nast pnie tekstura przekazywana jest do funkcji glTexImage2D:
// Ładowanie tekstury
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
pBytes = gltLoadTGA("Stone.tga", &iWidth, &iHeight, &iComponents, &eFormat);
glTexImage2D(GL_TEXTURE_2D, 0, iComponents, iWidth, iHeight, 0, eFormat, GL_UNSIGNED_BYTE, pBytes);
free(pBytes);
Oczywi cie musi te zosta wł czony mechanism odwzorowywania tekstur:
GlEnable(GL_TEXTURE_2D);
Funkcja RenderScene rysuje piramid jako zbiór pokrytych teksturami trójk tów. W poni szym wycinku kodu tworzona
jest jedna powierzchnia, dla której wyliczany jest wektor normalny (na podstawie wierzchołków), a tak e współrz dne
tekstury:
// Przednia strona
gltGetNormalVector(vCorners[0], vCorners[4], vCorners[3], vNormal);
glNormal3fv(vNormal);
glTexCoord2f(0.5f, 1.0f);
glVertex3fv(vCorners[0]);
glTexCoord2f(0.0f, 0.0f);
glVertex3fv(vCorners[4]);
glTexCoord2f(1.0f, 0.0f);
glVertex3fv(vCorners[3]);