OpenGL
Podstawy
Czym jest OpenGL ?
Czym jest OpenGL ?
●
"Programowy interfejs sprzętu graficznego"
●
Biblioteka zawierająca zbiór procedur ułatwiających
rysowanie grafiki dwu
i trójwymiarowej.
●
OpenGL nie jest, ani nie zawiera "enginu" grafiki
trójwymiarowej – służy jedynie do rysowania.
●
Procedury służące do rysowania skomplikowanych
modeli trójwymiarowych, ich animacji i transformacji
powinny być zaimplementowane przez programistę.
●
OpenGL nie zawiera funkcji służących do tworzenia
okien i ich zarządzania (od tego jest np. GLUT), ani do
interakcji z użytkownikiem.
●
Procedury OpenGL służą do rysowania prymitywów
(punktów, odcinków, trójkątów), które mogą być
cieniowane, pokryte teksturami, przeźroczyste itd.
●
Implementacje OpenGL w różnych środowiskach
mogą, ale nie muszą wykorzystywać akceleracji
sprzętowej. Wykorzystanie dostępnych mechanizmów
zależy od implementacji OpenGL i jest przeźroczyste
dla programisty.
Czym jest OpenGL ?
Czym jest OpenGL ?
Czym jest OpenGL ?
Czym jest OpenGL ?
●
Biblioteka OpenGL pracuje na zasadzie maszyny
stanów.
●
W praktyce polega to na tym, że w danym momencie
pracy jest ustawiony zbiór opcji wg. których rysowane
są prymitywy (np. kolor, sposób cieniowania itp.).
●
Sprawia to pewne problemy przy konstrukcji enginu do
np. gry komputerowej, gdyż zawsze, kiedy zaczynamy
rysować jakiś obiekt należy pamiętać, żeby ustawić
ponownie wszystkie parametry rysowania, gdyż
niektóre mogły zostać zmienione podczas rysowania
innych obiektów.
Nazwy procedur w OpenGL (1)
Nazwy procedur w OpenGL (1)
●
W OpenGL praktycznie każda procedura posiada kilka
wersji.
●
Wersje te różnią się przede wszystkim typem
parametrów.
●
Jeżeli procedura może mieć wersje różniące się liczbą
parametrów, to do nazw konkretnych wersji dodawany
jest przyrostek (cyfra) określający liczbę tych
parametrów.
Nazwy procedur w OpenGL (2)
Nazwy procedur w OpenGL (2)
●
W zależności od typu parametrów dodawane są
przyrostki, np:
–
d – double
–
f – float
–
i – int
●
Jeżeli istnieją wersje procedury, z których jedna
przyjmuje jako parametr liczbę, a druga wektor, to
dodawany jest również przyrostek v.
Nazwy procedur w OpenGL (3)
Nazwy procedur w OpenGL (3)
●
Przykłady:
–
glVertex3d – posiada trzy parametry typu double
–
glLightfv – jako jeden z parametrów przyjmuje wektor liczb
typu float
Czyszczenie okna (1)
Czyszczenie okna (1)
●
Wyczyszczenie zawartości okna polega na zapisaniu
każdego piksela takim samym kolorem.
●
Kolor czyszczenia można ustawić za pomocą
procedury glClearColor.
●
Składnia: void glClearColor( GLclampf red, GLclampf
green, GLclampf blue,
GLclampf alpha );
●
red, green, blue – to składowe koloru, alpha to stopień
przezroczystości.
Czyszczenie okna (2)
Czyszczenie okna (2)
●
Do czyszczenia okna służy procedura glClear.
●
Składnia: void glClear( GLbitfield mask )
●
Parametr mask jest mapą bitową tworzoną przez
superpozycję pewnych stałych. Określa które bufory
powinny być czyszczone. Np.:
–
GL_COLOR_BUFFER_BIT – obraz
–
GL_DEPTH_BUFFER_BIT - z-bufor
Wyświetlanie wyników rysowania
Wyświetlanie wyników rysowania
●
Jeżeli pracujemy w oknie z podwójnym buforowaniem
konieczna jest dodatkowa operacja aby wyświetlić
wynik rysowania.
●
W podwójnym buforowaniu rysujemy na niewidocznym
buforze. Aby go pokazać należy wywołać procedurę:
glutSwapBuffers;
●
Składnia: void glutSwapBuffers(void);
Ćwiczenie
Ćwiczenie
●
Do części inicjującej szkieletu dodaj procedurę
określającą kolor czyszczenia na niebieski.
●
Do procedury odświeżającej okno dodaj wywołanie
procedury czyszczącej okno (czyść zarówno bufor
kolorów jak i z-bufor (to drugie przyda się w
następnych ćwiczeniach). Nie zapomnij na końcu
procedury odświeżającej dodać wywołania procedury
glutSwapBuffers.
●
W wyniku ćwiczenia powinieneś otrzymać okno, które
jest wypełnione kolorem niebieskim, zamiast
przypadkowych obrazów.
Układy współrzędnych (1)
Układy współrzędnych (1)
Przestrzeń
obiektu
Przestrzeń
świata
Przestrzeń
oka
Przestrzeń
przycięcia
Znormalizowana
przestrzeń urządzenia
Przestrzeń
okna
Przekształcenie modelu
Przekształcenie widoku
Przekształcenie rzutowania
Podział perspektywiczny
Przekształcenie obrazu
i zakresu głębi
Układy współrzędnych (2)
Układy współrzędnych (2)
●
Współrzędne homogeniczne punktu
w trójwymiarowej przestrzeni są reprezentowane
przez wektor 4 liczb: <x,y,z,w>.
●
Normalne współrzędne trójwymiarowe można
uzyskać dzieląc współrzędne x, y i z przez w.
●
Kolejne przekształcenia są reprezentowane
przez macierze 4x4.
●
Dzięki dodatkowej współrzędnej transformacje
wymagające dodania stałej do współrzędnej x, y
albo z (np. przesunięcie) mogą być również
przedstawione w postaci macierzy.
Układy współrzędnych (3)
Układy współrzędnych (3)
●
Przestrzeń obiektu – układ współrzędnych
charakterystyczny dla pojedynczego rysowanego
obiektu.
●
Przestrzeń świata – układ współrzędnych rysowanej
sceny.
●
Przekształcenie modelu (model) – przekształcenie
przestrzeni obiektu w przestrzeń świata.
●
Przestrzeń oka – układ współrzędnych w którym
obserwator znajduje się w początku układu
współrzędnych, patrzy w kierunku rosnących wartości
współrzędnych z (w OpenGL malejących), a góra to
dodatni kierunek współrzędnej y.
Układy współrzędnych (4)
Układy współrzędnych (4)
●
Przekształcenie widoku (view) – przekształcenie
przestrzeni świata w przestrzeń oka.
●
Przestrzeń przycięcia – przestrzeń definiuje widziany
obszar. Wszystko co jest widoczne znajduje się
wewnątrz sześcianu o krawędziach równoległych do
osi układu współrzędnych. Każdy widoczny punkt musi
spełniać warunki: -w≤x≤w, -w≤y≤w, -w≤z≤w.
●
Przekształcenie rzutowania (projection) –
przekształcenie przestrzeni oka w przestrzeń
przycięcia.
Układy współrzędnych (5)
Układy współrzędnych (5)
●
Znormalizowana przestrzeń urządzenia –
w przestrzeni urządzenia wszystkie współrzędne
homogeniczne są przekształcone do ich reprezentacji
trójwymiarowej (w=1).
●
Podział perspektywiczny – transformacja przestrzeni
przycięcia do znormalizowanej przestrzeni urządzenia.
●
Przestrzeń okna – przestrzeń w której współrzędne są
wyrażone w pikselach w oknie,
w którym obraz jest ostatecznie rysowany.
Układy współrzędnych (6)
Układy współrzędnych (6)
●
W OpenGL macierze przekształceń modelu
i widoku są połączone w macierz model – widok będącą
złożeniem obu tych transformacji. Postąpiono w ten
sposób, gdyż w zasadzie jedna z tych macierzy może
zastąpić drugą.
●
Możemy modyfikować w dowolny sposób macierz model –
widok i macierz rzutowania.
●
Przekształcenie obrazu można modyfikować za pomocą
procedury glViewport, oraz za pomocą procedur biblioteki
GLUT. Tym przekształceniem nie będziemy się zajmować
na zajęciach.
●
Procedurę glViewport wykorzystuje się do zainicjowania
macierzy przekształcenia obrazu (robione automatycznie
przez GLUT) oraz w sytuacjach, gdy okno zmienia
rozmiar.
Układy współrzędnych (7)
Układy współrzędnych (7)
●
Aby przełączyć OpenGL w tryb modyfikacji
odpowiedniej macierzy należy wykorzystać
procedurę void glMatrixMode( GLenum mode ),
gdzie mode wyznacza którą z macierzy należy
modyfikować:
–
GL_PROJECTION – macierz rzutowania
–
GL_MODELVIEW – macierz model – widok.
Układy współrzędnych (8)
Układy współrzędnych (8)
●
Obie macierze modyfikuje się poprzez:
–
Załadowanie macierzy jednostkowej za pomocą procedury
void glLoadIdentity().
–
Mnożenie aktualnej macierzy przez macierze reprezentujące
różne transformacje.
●
W przypadku macierzy rzutowania, załadowaną
macierz jednostkową mnożymy razy macierz
rzutowania równoległego (procedura glOrtho) lub
macierz rzutu perspektywicznego (procedura
gluPerspective)
Układy współrzędnych (9)
Układy współrzędnych (9)
●
W ćwiczeniach będziemy korzystać jedynie
z rzutu perspektywicznego.
●
Procedura gluPerspective ma następującą składnię:
void gluPerspective( GLdouble fovy, GLdouble aspect,
GLdouble zNear,
GLdouble zFar ), gdzie:
–
fovy to kąt widzenia (stopnie)
–
aspect to stosunek szerokości okna do jego wysokości
–
zNear to odległość bliższej płaszczyzny obcinania
–
zFar to odległość dalszej płaszczyzny obcinania
Układy współrzędnych (10)
Układy współrzędnych (10)
●
Poniższy przykładowy kod należy umieścić w części
inicjującej szkieletu.
..........
glMatrixMode(GL_PROJECTION);
//Przełączenie w tryb macierzy rzutowania
glLoadIdentity();
//Załadowanie macierzy jednostkowej
gluPerspective(55,1,1,50);
//Pomnożenie jej razy macierz rzutu perspektywicznego
...........
●
Przykład ten konfiguruje OpenGL do wykonywania
rzutowania perspektywicznego, z kątem widzenia 55
stopni, stosunkiem wysokości do szerokości 1, bliskiej
płaszczyźnie obcinania w odległości 1 i dalekiej
płaszczyzny obcinania w odległości 50.
Układy współrzędnych (11)
Układy współrzędnych (11)
●
Macierz widok – model zawiera złożenie transformacji
translacji, obrotów i skalowania, które przekształca
współrzędne w przestrzeni modelu we współrzędne w
przestrzeni oka.
●
Procedury służące do obrotu układu współrzędnych,
translacji i skalowania zostaną podane później. Do
następnego ćwiczenia przyda się procedura gluLookAt
Układy współrzędnych (12)
Układy współrzędnych (12)
●
Procedura gluLookAt wykonuje taką transformację
układu współrzędnych, aby odpowiadał on widokowi
jaki posiada obserwator o określonym położeniu
patrzący na konkretny punkt – innymi słowy mnoży
aktualnie modyfikowaną macierz razy macierz
przekształcenia widoku.
●
Składnia: void gluLookAt(
GLdouble eyex, GLdouble eyey, GLdouble eyez,
GLdouble centerx, GLdouble centery, GLdouble
centerz,
GLdouble upx, GLdouble upy, GLdouble upz )
Układy współrzędnych (13)
Układy współrzędnych (13)
●
Poszczególne parametry tej procedury mają
następujące znaczenie:
–
eyex, eyey, eyez – współrzędne obserwatora
–
centerx, centery, centerz – obserwowany punkt
–
upx, upy, upz – wektor określający "górę" obserwatora
Układ współrzędnych (14)
Układ współrzędnych (14)
●
Przykładowy kod wykorzystujący opisane wcześniej
procedury przedstawiono poniżej. Można go umieścić
zarówno w części inicjującej jak i rysującej, ale od tego
zależy sposób wykonywania dalszych ćwiczeń.
Sugeruję umieszczenie go w części rysującej.
.............
glMatrixMode(GL_MODELVIEW);
//Przełączenie w tryb macierzy widoku modelu
glLoadIdentity();
//Załadowanie macierzy jednostkowej
gluLookAt(7,7,7,0,0,0,0,1,0);
//Patrzy z punktu <7,7,7> na <0,0,0>, głowa prosto ;)
.............
Prosty obiekt
Prosty obiekt
●
Istnieje wiele procedur rysujących proste obiekty. Oto
kilka z nich (nazwy są samotłumaczące ;))
–
void glutSolidSphere(GLdouble radius, GLint slices, GLint stacks);
–
void glutWireSphere(GLdouble radius, GLint slices, GLint stacks);
–
void glutSolidCube(GLdouble size);
–
void glutWireCube(GLdouble size);
–
void glutSolidCone(GLdouble base, GLdouble height, GLint slices, GLint
stacks);
–
void glutWireCone(GLdouble base, GLdouble height, GLint slices, GLint
stacks);
–
void glutSolidTorus(GLdouble innerRadius, GLdouble outerRadius, GLint
nsides, GLint rings);
–
void glutWireTorus(GLdouble innerRadius, GLdouble outerRadius, GLint
nsides, GLint rings);
–
void glutSolidTeapot(GLdouble size);
–
void glutWireTeapot(GLdouble size);
Ćwiczenie
Ćwiczenie
●
Ćwiczenie polega na modyfikacji wyniku poprzedniego
ćwiczenia.
●
Zmień kolor tła na czarny.
●
Narysuj szkielet torusa (glutWireTorus)
o promieniu wewnętrznym 1 a zewnętrznym 3. Nie
zapomnij o ustawieniu obserwatora!
●
Wywołanie glutWireTorus powinieneś
umieścić w procedurze rysującej po
ustawieniu obserwatora, ale przed
glSwapBuffers;
Transformacje układu współrzędnych (1)
Transformacje układu współrzędnych (1)
●
Obrót obiektu można wykonać poprzez pomnożenie
macierzy model – widok przez macierz obrotu.
●
Procedura void glRotated( GLdouble angle, GLdouble
x, GLdouble y, GLdouble z ) mnoży aktualnie
modyfikowaną macierz przez macierz obrotu o kąt
angle wokół osi wyznaczonej przez wektor <x,y,z>.
Transformacje układu współrzędnych (2)
Transformacje układu współrzędnych (2)
●
Procedura void glScaled( GLdouble x, GLdouble y,
GLdouble z ) mnoży aktualnie modyfikowaną macierz
razy macierz skalującą. Kolejne parametry procedury
to współczynniki skalowania na poszczególnych
osiach układu współrzędnych.
●
Procedura void glTranslated( GLdouble x, GLdouble y,
GLdouble z ) mnoży aktualnie modyfikowaną macierz
razy macierz translacji. Kolejne parametry procedury
to współrzędne wektora, o który następuje
przesunięcie.
Ćwiczenie
Ćwiczenie
●
Umieść przed instrukcją rysującą torus (ale za
instrukcjami definiującymi obserwatora) instrukcję
obracającą układ współrzędnych dookoła osi Y
(wektor <0,1,0>) o 60 stopni.
Animacja (1)
Animacja (1)
●
Animację można zrealizować za pomocą specjalnej
procedury callback wywoływanej najczęściej jak się
da.
●
Do ustawienia tej procedury służy procedura:
void glutIdleFunc(void (*func)(void));
●
Jako parametr powinno się przekazać nazwę
procedury, która nie posiada parametrów. Procedura
ta będzie wywoływana przez glut.
Animacja (2)
Animacja (2)
●
Procedura callback powinna modyfikować wartości
jakichś zmiennych na podstawie których rysowany jest
obraz w procedurze rysującej i wywołać ponowne
narysowanie obrazu.
●
Aby wywołać ponowne narysowanie obrazu należy
wywołać procedurę:
void glutPostRedisplay(void);
Animacja (3)
Animacja (3)
●
Przykład fragmentów kodu definiujących animację:
float
speed=360;
//360 stopni/s
int
lastTime=0;
float
angle;
void
displayFrame(
void
) {
............
glRotated(angle,1,0,0);
glutWireTorus(1,3,10,10);
glutSwapBuffers();
}
void
nextFrame(void) {
int
actTime=glutGet(GLUT_ELAPSED_TIME);
int
interval=actTime-lastTime;
lastTime=actTime;
angle+=speed*interval/1000.0;
if
(angle>360) angle-=360;
glutPostRedisplay();
}
int
main (....) {
.......
glutIdleFunc(nextFrame);
.......
glutMainLoop();
}
Ćwiczenie
Ćwiczenie
●
Zmodyfikuj poprzedni program tak, aby torus obracał się wokół
dwóch osi (X i Y). Wokół osi X z prędkością 120 stopni/s a
wokół osi Y z prędkością 240 stopni/s.
Kolory
Kolory
●
Kolor obiektu który ma być narysowany ustawia się za
pomocą procedury: void glColor3d( GLdouble red,
GLdouble green, GLdouble blue )
●
Poszczególne parametry oznaczają udział składowych
w wynikowym kolorze i przyjmują wartości z przedziału
od 0 do 1.
Ćwiczenie
Ćwiczenie
●
Zmodyfikuj poprzedni program tak, aby animowany
torus był niebieski.
●
Spróbuj zastąpić procedurę rysującą siatkę torusa
procedurą rysującą pełen torus (glutSolidTorus). Czy
efekt jest zadowalający ?
Cieniowanie (1)
Cieniowanie (1)
●
Aby włączyć cieniowanie należy wykonać nastepującą
instrukcję:
glEnable(GL_LIGHTING);
●
glEnable służy do włączania wielu różnych opcji,
które będą stopniowo wprowadzane.
●
Po włączeniu cieniowania należy włączyć źródło
światła:
glEnable(GL_LIGHT0);
●
Domyślnie źródło światła o numerze 0 ma kolor
biały i znajduje się w nieskończoności, za
obserwatorem.
Cieniowanie (2)
Cieniowanie (2)
●
Kolejnym krokiem jest włączenie typu cieniowania.
●
Zaimplementowane są dwa algorytmy: płaskie
i gładkie (Gourauda).
●
Typ cieniowania wybieramy za pomocą procedury void
glShadeModel( GLenum mode ), która przyjmuje
parametry:
–
GL_FLAT – cieniowanie płaskie
–
GL_SMOOTH – cieniowanie gładkie
Cieniowanie (3)
Cieniowanie (3)
●
Przykładowy kod należy umieścić w części
inicjującej:
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glShadeModel(GL_SMOOTH);
●
Powyższy kod włącza cieniowanie, zerowe źródło
światła i ustawia cieniowanie gładkie
Z-Bufor
Z-Bufor
●
Z-Bufor służy do usuwania niewidocznych
powierzchni.
●
Bufor ten przechowuje informację w jakiej odległości
od obserwatora znajduje się aktualnie narysowany
piksel.
●
Jeżeli kolejny rysowany w tym miejscu piksel jest
dalej, to jest on pomijany, gdyż obecny przesłania go.
●
Aby włączyć Z-Bufor należy wykonać instrukcję:
glEnable(GL_DEPTH_TEST);
//W części inicjującej
Wstęp do materiałów
Wstęp do materiałów
●
Każdy obiekt jest zbudowany z "materiału"
●
Materiałem nazywamy zbiór parametrów
określających sposób cieniowania obiektu, oraz np.
jego teksturę.
●
Aby obiekt złożony z wielokątów (a nie jego szkielet)
miał kolor taki jak aktywny należy włączyć śledzenie
przez materiał kolorów za pomocą instrukcji:
glEnable(GL_COLOR_MATERIAL);
//W części inicjującej
Ćwiczenie
Ćwiczenie
●
Zastąp instrukcję rysującą szkielet torusa instrukcją
rysującą pełen torus (auxSolidTorus) jeśli tego nie
zrobiłeś w poprzednim ćwiczeniu.
●
Włącz cieniowanie, zerowe światło, typ cieniowania
płaski.
●
Włącz Z-Bufor i śledzenie kolorów przez materiał.
●
Zmień model cieniowania na gładki. Zaobserwuj
różnice.
Interakcja z użytkownikiem (1)
Interakcja z użytkownikiem (1)
●
Biblioteka GLUT pozwala na zdefiniowanie procedury
callback wywoływanej w momencie naciśnięcia
klawisza.
●
Procedura ta powinna posiadać następującą
sygnaturę: void keyEvent(unsigned char c, int x, int y).
Oczywiście nazwa jest dowolna.
●
Procedurę callback rejestruje się za pomocą
procedury void glutKeyboardFunc(
void (*func)(unsigned char key,int x, int y));
Interakcja z użytkownikiem (2)
Interakcja z użytkownikiem (2)
●
Podobnie jak w przypadku animacji wywołana
procedura powinna modyfikować parametry
wyświetlania klatki przez procedurę rysującą.
●
Przykład:
void
keyEvent(
unsigned
char
c,
int
x,
int
y) {
if
(c=='a') angle=(angle+5)%360;
if
(c=='z') angle=(angle-5)%360;
glutPostRedisplay();
}
int
main (....) {
.......
glutKeyboardFunc(keyEvent);
.......
glutMainLoop();
}
Ćwiczenie
Ćwiczenie
●
Zakomentuj zawartość procedury nextFrame.
●
Zastąp procedurę rysującą torus inną procedurą,
rysującą jakiś obiekt trójwymiarowy.
●
Utwórz procedurę callback pozwalającą na obracanie
tym obiektem wokół wszystkich trzech osi układu
współrzędnych.
Stos macierzy (1)
Stos macierzy (1)
●
W trakcie rysowania jednej klatki można wielokrotnie
modyfikować macierz widoku modelu w celu
rysowania różnych obiektów.
●
Można sobie ułatwić te transformacje zapamiętując
aktualny stan macierzy. Do zapamiętania aktualnej
macierzy można wykorzystać stos macierzy.
●
Stos macierzy jest obsługiwany za pomocą
bezparametrowych procedur glPushMatrix()
i glPopMatrix().
Stos macierzy (2)
Stos macierzy (2)
●
Procedura glPushMatrix powoduje skopiowanie
i umieszczenie na stosie aktualnej macierzy.
●
Procedura glPopMatrix powoduje odczytanie ze stosu
macierzy i zastąpienie nią aktualnej macierzy.
●
Na następnym slajdzie zostanie przedstawiony
przykład wykorzystujący procedurę glutSolidCube.
Procedura ta rysuje sześcian. Jej jedynym
parametrem jest długość krawędzi.
Stos macierzy (3)
Stos macierzy (3)
●
Poniższy przykład rysuje obiekt złożony z wielu
sześcianów:
glColor3d(1,0,0);
glutSolidCube(1);
glColor3d(0,1,0);
glPushMatrix();
glTranslatef(0.5,0,0);
glutSolidCube(0.25);
glPopMatrix();
glPushMatrix();
glTranslatef(-0.5,0,0);
glutSolidCube(0.25);
glPopMatrix();
glPushMatrix();
glTranslatef(0,0.5,0);
glutSolidCube(0.25);
glPopMatrix();
glPushMatrix();
glTranslatef(0,-0.5,0);
glutSolidCube(0.25);
glPopMatrix();
glPushMatrix();
glTranslatef(0,0,0.5);
glutSolidCube(0.25);
glPopMatrix();
glPushMatrix();
glTranslatef(0,0,-0.5);
glutSolidCube(0.25);
glPopMatrix();
Ćwiczenie
Ćwiczenie
●
Zastanów się jak wygląda obiekt rysowany przez kod z
poprzedniego slajdu.
●
Zastąp dotychczasowy kod rysujący torus lub inny
obiekt kodem z przykładu. Czy takiego wyniku
oczekiwałeś ?
●
Zastanów się jak stworzyć dwa torusy obracające się
jak „w trybach”:
Rysowanie dowolnych obiektów (1)
Rysowanie dowolnych obiektów (1)
●
Dotychczas korzystaliśmy z gotowych procedur, które
rysowały całe obiekty – sześciany, torusy
i inne.
●
Można jednak narysować dowolny obiekt korzystając z
takich składowych jak: linie, trójkąty i czworokąty itp.
●
Aby narysować jakiś obiekt, można wykorzystać
procedury glDrawArrays i glDrawElements.
Rysowanie dowolnych obiektów (2)
Rysowanie dowolnych obiektów (2)
●
Rysowanie z użyciem glDrawArrays:
float smallQuadVertices[]={ //Tablica współrzędnych wierzchołków
-1,-1,0,
1,-1,0,
1, 1,0,
-1, 1,0
};
float smallQuadColors[]={ //Tablica kolorów wierzchołków
1,0,0,
1,0,0,
1,0,0,
1,0,0
};
int smallQuadVertexCount=4; //Liczba wierzchołków w tablicy
....................
glEnableClientState( GL_VERTEX_ARRAY );
glEnableClientState( GL_COLOR_ARRAY );
glVertexPointer( 3, GL_FLOAT, 0, smallQuadVertices );
glColorPointer( 3, GL_FLOAT, 0, smallQuadColors );
glDrawArrays( GL_QUADS, 0, smallQuadVertexCount );
glDisableClientState( GL_VERTEX_ARRAY );
glDisableClientState( GL_COLOR_ARRAY );
Rysowanie dowolnych obiektów (3)
Rysowanie dowolnych obiektów (3)
float geomVertices[]={
0,4.08,0, 0,0,2.88,
0,4.08,0, 2.5,0,-1.44,
0,4.08,0, -2.5,0,-1.44,
0,0,2.88, -2.5,0,-1.44,
0,0,2.88, 2.5,0,-1.44,
-2.5,0,-1.44, 2.5,0,-1.44
};
int geomVertexCount=12;
..........
glColor3d(0.5,1,0.5);
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer( 3, GL_FLOAT, 0,
geomVertices);
glDrawArrays(GL_LINES,0,geomVertexCount);
glDisableClientState(GL_VERTEX_ARRAY);
float geomVertices[]={
0,4.08,0, 0,0,2.88, -2.5,0,-1.44,
0,4.08,0, 0,0,2.88, 2.5,0,-1.44,
0,4.08,0, 2.5,0,-1.44,-2.5,0,-1.44,
2.5,0,-1.44, -2.5,0,-1.44, 0,0,2.88
};
float geomColors[]={
1,0,0, 1,0,0, 1,0,0,
0,1,0, 0,1,0, 0,1,0,
0,0,1, 0,0,1, 0,0,1,
1,1,0, 1,1,0, 1,1,0
};
int geomVertexCount=12;
.............
glEnableClientState( GL_VERTEX_ARRAY );
glEnableClientState( GL_COLOR_ARRAY );
glVertexPointer( 3, GL_FLOAT, 0,
geomVertices);
glColorPointer( 3, GL_FLOAT, 0,
geomColors);
glDrawArrays(GL_TRIANGLES,0,
geomVertexCount);
glDisableClientState( GL_VERTEX_ARRAY );
glDisableClientState( GL_COLOR_ARRAY );
Rysowanie dowolnych obiektów (4)
Rysowanie dowolnych obiektów (4)
●
Rysowanie z użyciem glDrawElements:
float geomVertices[]={
0,4.08,0, 0,0,2.88, -2.5,0,-1.44, 2.5,0,-1.44,
};
float geomColors[]={
1,0,0, 0,1,0, 0,0,1, 1,1,0
};
unsigned int geomIndexes[] ={
0,1,2, 0,1,3, 0,2,3, 3,2,1
};
int geomIndexCount=12;
int geomVertexCount=4;
...............
glEnableClientState( GL_VERTEX_ARRAY );
glEnableClientState( GL_COLOR_ARRAY );
glVertexPointer( 3, GL_FLOAT, 0, geomVertices);
glColorPointer( 3, GL_FLOAT, 0, geomColors);
glDrawElements(GL_TRIANGLES,geomIndexCount, GL_UNSIGNED_INT,geomIndexes);
glDisableClientState( GL_VERTEX_ARRAY );
glDisableClientState( GL_COLOR_ARRAY );
Vertex Buffer Objects (1)
Vertex Buffer Objects (1)
●
Rysując za pomocą glDrawArrays/glDrawElements za
każdym razem przesyłamy współrzędne wierzchołków
z pamięci komputera do pamięci karty graficznej. Dla
dużych modeli może to nie być wydajne.
●
Możliwe jest załadowanie tablic wykorzystywanych
przez procedury glDrawArrays/glDrawElements do
pamięci karty graficznej za pomocą tzw. Vertex Buffer
Objects.
Vertex Buffer Objects(2)
Vertex Buffer Objects(2)
●
Wykorzystanie VBO razem z glDrawArrays()
–
Załadowanie tablic do VBO:
//Tablice takie jak dla ostatniego przykładu z glDrawArrays()
GLuint geomVerticesId;
Gluint geomColorsId;
.......................
//Wygenerowanie uchwytu na "bufor"
glGenBuffers(1,&geomVerticesId);
//Wybranie aktywnego "bufora"
glBindBuffer(GL_ARRAY_BUFFER,geomVerticesId);
//Przeniesienie danych do bufora
glBufferData(GL_ARRAY_BUFFER,
geomVertexCount*3*sizeof(float), geomVertices, GL_STATIC_DRAW );
glGenBuffers(1,&geomColorsId);
glBindBuffer(GL_ARRAY_BUFFER,geomColorsId);
glBufferData(GL_ARRAY_BUFFER,
geomVertexCount*3*sizeof(float), geomColors, GL_STATIC_DRAW );
Vertex Buffer Objects (3)
Vertex Buffer Objects (3)
●
Wykorzystanie VBO razem glDrawArrays()
–
Wykorzystanie VBO podczas rysowania:
glEnableClientState( GL_VERTEX_ARRAY );
glEnableClientState( GL_COLOR_ARRAY );
glBindBuffer(GL_ARRAY_BUFFER,geomVerticesId);
glVertexPointer( 3, GL_FLOAT, 0, NULL);
glBindBuffer(GL_ARRAY_BUFFER,geomColorsId);
glColorPointer( 3, GL_FLOAT, 0, NULL);
glBindBuffer(GL_ARRAY_BUFFER,0);
glDrawArrays( GL_TRIANGLES, 0, geomVertexCount );
glDisableClientState( GL_VERTEX_ARRAY );
glDisableClientState( GL_
COLOR_ARRAY );
Vertex Buffer Objects (4)
Vertex Buffer Objects (4)
●
Wykorzystanie VBO razem z glDrawElements()
–
Załadowanie tablicy do VBO
//Tablice takie jak w ostatnim przykłądzie dla glDrawElements()
GLuint geomVerticesId;
GLuint geomColorsId;
GLuint geomIndexesId;
...........
glGenBuffers(1,&geomVerticesId);
glBindBuffer(GL_ARRAY_BUFFER,geomVerticesId);
glBufferData(GL_ARRAY_BUFFER,
geomVertexCount*3*sizeof(float), geomVertices, GL_STATIC_DRAW );
glGenBuffers(1,&geomColorsId);
glBindBuffer(GL_ARRAY_BUFFER,geomColorsId);
glBufferData(GL_ARRAY_BUFFER,
geomVertexCount*3*sizeof(float), geomColors, GL_STATIC_DRAW );
glGenBuffers(1,&geomIndexesId);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,geomIndexesId);
glBufferData(GL_ELEMENT_ARRAY_BUFFER,
geomIndexCount*3*sizeof(unsigned int), geomIndexes, GL_STATIC_DRAW );
Vertex Buffer Objects (5)
Vertex Buffer Objects (5)
●
Wykorzystanie VBO razem glDrawElements()
–
Wykorzystanie VBO podczas rysowania:
glEnableClientState( GL_VERTEX_ARRAY );
glEnableClientState( GL_COLOR_ARRAY );
glBindBuffer(GL_ARRAY_BUFFER,geomVerticesId);
glVertexPointer( 3, GL_FLOAT, 0, NULL);
glBindBuffer(GL_ARRAY_BUFFER,geomColorsId);
glColorPointer( 3, GL_FLOAT, 0, NULL);
glBindBuffer(GL_ARRAY_BUFFER,0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,geomIndexesId);
glDrawElements( GL_TRIANGLES, geomIndexCount, GL_UNSIGNED_INT, NULL );
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);
glDisableClientState( GL_VERTEX_ARRAY );
glDisableClientState( GL_COLOR_ARRAY );
Vertex Buffer Objects (6)
Vertex Buffer Objects (6)
●
Usuwanie bufora VBO z pamięci:
glDeleteBuffers(1,&geomVerticesId);
Ćwiczenie
Ćwiczenie
●
Zakomentuj kod włączający cieniowanie (włączenie
cieniowania, włączenie światła
i ustawienie typu cieniowania).
●
Napisz kod, który rysuje sześcian. Opcjonalnie użyj
VBO. Każda ściana powinna być innego koloru.
Teksturowanie (1)
Teksturowanie (1)
●
Teksturowaniem nazywamy nakładanie na ściany
wyświetlanych brył obrazków tekstur (potocznie
nazywanych po prostu "teksturami").
●
W OpenGL istnieją mechanizmy pozwalające na łatwe
oteksturowanie modelu, ale nie ma procedur
wczytujących tekstury z plików graficznych – to trzeba
zaimplementować samemu.
●
Podczas ćwiczeń wykorzystamy bibliotekę do odczytu
plików TGA, którą można sciągnąć z:
http://gpwiki.org/index.php/LoadTGACpp
Teksturowanie (2)
Teksturowanie (2)
●
Biblioteka definiuje klasę TGAImg, która zawiera
metody takie jak:
–
int Load(char* szFileName) – wczytuje plik, zwraca wartość
IMG_OK, jeśli obraz odczyta się w porządku
–
int GetBPP() - zwraca liczbę bitów na piksel
–
int GetWidth() - zwraca szerokość obrazka
–
int GetHeight() - zwraca wysokość obrazka
–
unsigned char* GetImg() - zwraca wskaźnik do danych
obrazka
–
unsigned char* GetPalette() - zwraca wskaźnik do danych
palety obrazka
Teksturowanie (3)
Teksturowanie (3)
●
Kiedy obraz zostanie wczytany do pamięci należy go
zaimportować do OpenGL.
●
Wykonuje się to za pomocą trzech funkcji OpenGL:
–
glGenTextures()
–
glBindTexture()
–
glTexImage2D()
Teksturowanie (4) - glGenTextures
Teksturowanie (4) - glGenTextures
●
Służy do inicjowania uchwytów tekstur.
●
void glGenTextures(GLsizei n, GLuint *textures)
–
n – liczba uchwytów do zainicjowania
–
textures – tablica zmiennych typu Gluint, która ma
przechowywać nowe uchwyty
●
Jeżeli inicjujemy tylko jeden uchwyt można
zastosować następujący kod:
GLuint tex;
//Uchwyt
..................
glGenTextures(1,&tex);
//Zainicjuj jeden uchwyt
Teksturowanie (5) - glBindTexture
Teksturowanie (5) - glBindTexture
●
Ustawia aktualny uchwyt tesktury.
●
void glBindTexture(GLenum target,
GLuint texture);
–
target – typ tekstury, na naszych zajęciach zawsze
GL_TEXTURE_2D
–
texture – uchwyt, który ma być aktualny
Teksturowanie (6) - glTexImage2D
Teksturowanie (6) - glTexImage2D
●
Wczytuje obrazek do OpenGL, obrazek przechowywany
w obiekcie klasy TGAImg można po wykonaniu tej
procedury usunąć z pamięci.
●
void glTexImage2D( Glenum target, GLint level,
GLint internalformat, GLsizei width, GLsizei height,
GLint border, GLenum format, GLenum type,
const GLvoid *pixels );
–
target – zawsze GL_TEXTURE_2D
–
level – poziom mipmapy, dla nas zawsze 0
–
internalformat – liczba składowych koloru:
●
Dla obrazków 32 bitowych 4 (RGBA)
●
Dla obrazków 24 bitowych 3 (RGB)
Teksturowanie (7) – glTexImage2D
Teksturowanie (7) – glTexImage2D
–
width – szerokość obrazka (potęga dwójki)
–
height – wysokość obrazka (potęga dwójki)
–
border – na zajęciach zawsze 0
–
format:
●
GL_RGBA dla obrazków 32 bitowych
●
GL_RGB dla obrazków 24 bitowych
–
type – na zajęciach GL_UNSIGNED_BYTE
–
pixels – wskaźnik do obszaru pamięci, w którym zapisano
dane obrazka, można go uzyskać korzystając z metody
GetImg klasy TGAImg.
Teksturowanie (8)
Teksturowanie (8)
●
Do załadowania pojedynczej tekstury można użyć
następującego kodu:
if
(img.Load(TexName)==IMG_OK) {
glGenTextures(1,&tex);
//Zainicjuj uchwyt tex
glBindTexture(GL_TEXTURE_2D,tex);
//Przetwarzaj uchwyt tex
if
(img.GetBPP()==24)
//Obrazek 24bit
glTexImage2D(GL_TEXTURE_2D,0,3,img.GetWidth(),img.GetHeight(),0,
GL_RGB,GL_UNSIGNED_BYTE,img.GetImg());
else
if
(img.GetBPP()==32)
//Obrazek 32bit
glTexImage2D(GL_TEXTURE_2D,0,4,img.GetWidth(),img.GetHeight(),0,
GL_RGBA,GL_UNSIGNED_BYTE,img.GetImg());
else
{
//Obrazek 16 albo 8 bit, takimi się nie przejmujemy
}
}
else
{
//błąd
}
Teksturowanie (9) - glDeleteTextures
Teksturowanie (9) - glDeleteTextures
●
Zwalnia pamięć zajmowaną przez tekstury
o zadanych uchwytach.
●
void glDeleteTextures(GLsizei n,
GLuint *textures)
–
n – liczba uchwytów do zwolnienia
–
textures – tablica zmiennych typu Gluint, która przechowuje
usuwane uchwyty
Teksturowanie (10)
Teksturowanie (10)
●
Aby móc używać tekstur przy rysowaniu obrazów należy jeszcze ustawić
kilka parametrów teksturowania:
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_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
●
Powyższe parametry ustawiają zawijanie tekstur i filtrowanie dwuliniowe
(bilinear filtering) bez mipmap. Powyższe ustawienia będziemy
wykorzystywać podczas zajęć, ale sugeruję poeksperymentowanie z nimi w
domu. Ustawienia te można zmienić w dowolnym momencie działania
programu.
Teksturowanie (11)
Teksturowanie (11)
●
Ostatnią czynnością jaką należy wykonać przed
rysowaniem teksturowanych obiektów jest włączenie
teksturowania:
glEnable(GL_TEXTURE_2D)
●
W trakcie pracy programu można włączać
i wyłączać (glDisable) teksturowanie.
Teksturowanie (12)
Teksturowanie (12)
●
Aby rysowany obiekt pokryć teksturą należy
z każdym jego wierzchołkiem skojarzyć współrzędne
tekstury mu odpowiadające.
●
Aby móc podać współrzędne teksturowania należy
umieścić je w tablicy, a następnie wskazać je
poleceniu glDrawArrays/glDrawElements za pomocą
glTexCoordPointer.
●
Aby aktywować użycie wspórzędnych teksturowania,
należy użyć polecenia:
glEnableClientState(GL_TEXTURE_COORD_ARRAY)
Teksturowanie (13)
Teksturowanie (13)
Poniższy przykład rysuje oteksturowany kwadrat:
float geomVertices[]={
-1,-1,0, 1,-1, 0, 1,1,0, -1,1,0
};
float geomTexCoords[]={
0,0, 1,0, 1,1, 0,1
};
int geomVertexCount=4;
............
glBindTexture(GL_TEXTURE_2D,tex);
glEnableClientState( GL_VERTEX_ARRAY );
glEnableClientState( GL_TEXTURE_COORD_ARRAY );
glVertexPointer( 3, GL_FLOAT, 0, geomVertices);
glTexCoordPointer( 2, GL_FLOAT, 0, geomTexCoords);
glDrawArrays(GL_QUADS,0, geomVertexCount);
glDisableClientState( GL_VERTEX_ARRAY );
glDisableClientState( GL_TEXTURE_COORD_ARRAY );
Ćwiczenie
Ćwiczenie
●
Zmodyfikuj program z poprzednich ćwiczeń tak, aby
rysował i animował teksturowany sześcian. Na
potrzeby ćwiczenia wyłącz cieniowanie.
Zaawansowane cieniowanie (1)
Zaawansowane cieniowanie (1)
●
Aby OpenGL poprawnie obliczał ile światła przypada
na każdą ścianę lub każdy wierzchołek bryły, musi
znać normalne do powierzchni.
●
Normalna jest wektorem który jest prostopadły do
powierzchni. Normalną można obliczyć znając trzy
punkty definiujące tę powierzchnię.
●
OpenGL wymaga, aby wektory normalne miały
jednostkową długość.
Zaawansowane cieniowanie (2)
Zaawansowane cieniowanie (2)
●
Punkty definiujące powierzchnię stanowią początek i 2
końce dwóch wektorów.
●
Mając dane współrzędne wektorów a i b,
nieznormalizowany wektor prostopadły do płaszczyzny
można znaleźć obliczając wyznacznik przedstawiony
na następnym slajdzie.
n
a
b
Zaawansowanie cieniowanie (3)
Zaawansowanie cieniowanie (3)
●
Normalną można uzyskać z następującego
wyznacznika:
gdzie i, j i k to wektory tworzące układ współrzędnych,
odpowiednio <1,0,0>, <0,1,0> i <0,0,1>.
Uwaga ! Zamiana wektorów powoduje zmianę zwrotu
normalnej na przeciwny !
n=
∣
i
j
k
a
x
a
y
a
z
b
x
b
y
b
z
∣
Zaawansowane cieniowanie (4)
Zaawansowane cieniowanie (4)
●
Aby znormalizować długość wektora normalnego
należy wszystkie jego współrzędne podzielić przez
długość wektora.
●
Można również włączyć automatyczną normalizację
długości wektora normalnego za pomocą wywołania:
–
glEnable(GL_NORMALIZE);
Zawansowane cieniowanie (5)
Zawansowane cieniowanie (5)
●
Współrzędne wektora normalnego podajemy podobnie
jak współrzędne tekstur, kolory, i współrzędne
wierzchołków – potrzebna jest tablica z odpowiednimi
danymi.
●
Tablicę wiążemy z poleceniem glDrawArrays lub
glDrawElements za pomocą glNormalPointer.
●
Aby aktywować użycie tablicy z normalnymi, należy
użyć polecenia:
glEnableClientState(GL_NORMAL_ARRAY)
Zaawansowane cieniowanie (6)
Zaawansowane cieniowanie (6)
●
Przykład narysowania oteksturowanego i
ocieniowanego kwadratu:
float geomVertices[]={
-1,-1,0, 1,-1, 0, 1,1,0, -1,1,0
};
float geomTexCoords[]={
0,0, 1,0, 1,1, 0,1
};
float geomNormals[]={
0,0,1, 0,0,1, 0,0,1, 0,0,1
};
int geomVertexCount=4;
............
glBindTexture(GL_TEXTURE_2D,tex);
glEnableClientState( GL_VERTEX_ARRAY );
glEnableClientState( GL_TEXTURE_COORD_ARRAY );
glEnableClientState( GL_NORMAL_ARRAY );
glVertexPointer( 3, GL_FLOAT, 0, geomVertices);
glNormalPointer( GL_FLOAT, 0, geomNormals);
glTexCoordPointer( 2, GL_FLOAT, 0, geomTexCoords);
glDrawArrays(GL_QUADS,0, geomVertexCount);
glDisableClientState( GL_VERTEX_ARRAY );
glDisableClientState( GL_TEXTURE_COORD_ARRAY );
glDisableClientState( GL_NORMAL_ARRAY );
Ćwiczenie
Ćwiczenie
Zmodyfikuj program z poprzedniego ćwiczenia dodając definicje
wektorów normalnych ścian. Usuń instrukcję włączającą śledzenie
kolorów przez ściany: glEnable(GL_COLOR_MATERIAL). Dla
każdej ściany podaj inną normalną (wszystkie wierzchołki danej
ściany powinny mieć taką samą normalną). Włącz cieniowanie
gładkie. Co zauważyłeś ?
Zaawansowane cieniowanie (7)
Zaawansowane cieniowanie (7)
●
Jak można zauważyć w ćwiczeniu, pomimo tego, że
włączone jest cieniowanie gładkie, każda ściana
w dowolnym miejscu dostaje tyle samo światła.
●
Wynika to z faktu, że normalna jest zdefiniowana dla
całej ściany, a zatem ilość światła padającego jest
obliczana na całej ścianie tak samo (światło znajduje
się w nieskończoności za obserwatorem).
●
Aby poprawić jakość cieniowania należy zdefiniować
inną normalną dla każdego wierzchołka.
Zaawansowane cieniowanie (8)
Zaawansowane cieniowanie (8)
●
Matematycznie nie istnieje coś takiego jak normalna
wierzchołka!
●
W praktyce stosujemy znormalizowaną średnią
arytmetyczną normalnych wszystkich ścian
spotykających się w danym wierzchołku.
Ćwiczenie
Ćwiczenie
●
Zmodyfikuj poprzednie ćwiczenie definiując teraz
normalną dla każdego wierzchołka, jako średnią
arytmetyczną normalnych sąsiadujących ścian.
Zaawansowane cieniowanie (9)
Zaawansowane cieniowanie (9)
●
Zbiór parametrów optycznych rysowanej ściany, które są
wykorzystywane we wzorze modelu oświetlenia nazywamy
materiałem.
●
W skład materiału wchodzą następujące parametry:
–
Ambient – określa w jakim stopniu odbijane jest światło otoczenia
–
Diffuse – określa w jakim stopniu odbijane jest światło
rozproszone
–
Specular – określa w jakim stopniu występują odbicia lustrzane
–
Emmision – określa emitowane światło
–
Shininess – określa połyskliwość ściany
Zaawansowane cieniowanie (10)
Zaawansowane cieniowanie (10)
●
Aby ustawić parametry ambient, dissuse, specular i
emission stosujemy procedurę:
●
void glMaterialfv( GLenum face, Glenum pname, const
GLfloat *param );
–
face – określa której strony ściany dotyczy ustawiany
parametr – GL_FRONT, GL_BACK,
GL_FRONT_AND_BACK.
Zaawansowane cieniowanie (11)
Zaawansowane cieniowanie (11)
–
pname – modyfikowany parametr materiału:
●
GL_AMBIENT
●
GL_DIFFUSE
●
GL_SPECULAR
●
GL_EMISSION
●
GL_AMBIENT_AND_DIFFUSE
–
param – wskaźnik na tablicę 4 liczb typu Glfloat
z przedziału <0,1>, które definiują kolejno wpływ własności
materiału na składowe światła: czerwoną, zieloną, niebieską
i alfa.
Zaawansowane cieniowanie (12)
Zaawansowane cieniowanie (12)
●
Parametr shininess można zmienić za pomocą
procedury:
●
void glMaterialf( GLenum face, Glenum pname, const
GLfloat param );
–
face – tak samo jak dla glMaterialfv
–
pname – musi być GL_SHININESS
–
param – wartość wykładnika phonga (czyli połyskliwość
materiału)
Ćwiczenie 1
Ćwiczenie 1
Zmodyfikuj procedurę rysującą sześcian tak, aby ustalić
wektory normalne dla każdego wierzchołka. Ustaw
następujące parametry materiału:
–
ambient i emmision {0,0,0,1}
–
diffuse {0.7,0.5,0.5,1}
–
specular {0.5,0.5,0.5,1}
–
shininess 50
Zastanów się, dlaczego odbicie lustrzane można
zobaczyć tylko w sytuacji, gdy wierzchołek sześcianu jest
skierowany na obserwatora. Poeksperymentuj trochę z
parametrami materiału.
Ćwiczenie 2 (1)
Ćwiczenie 2 (1)
●
Przekopiuj poniższy kod i wykonaj procedurę initWall
w funkcji main().
//Zadeklaruj globalnie
float *geomVertices;
float *geomTexCoords;
float *geomNormals;
int geomVertexCount;
void quad(int subdiv,int i1, int i2, float x, float y, float back, float nx,
float ny, float s,float t,int pos){
geomVertices[i1*subdiv*3*4+i2*3*4+0+pos*3]=x;
geomVertices[i1*subdiv*3*4+i2*3*4+1+pos*3]=y;
geomVertices[i1*subdiv*3*4+i2*3*4+2+pos*3]=-back;
geomNormals[i1*subdiv*3*4+i2*3*4+0+pos*3]=nx;
geomNormals[i1*subdiv*3*4+i2*3*4+1+pos*3]=ny;
geomNormals[i1*subdiv*3*4+i2*3*4+2+pos*3]=-1.0/3;
geomTexCoords[i1*subdiv*2*4+i2*2*4+0+pos*2]=s;
geomTexCoords[i1*subdiv*2*4+i2*2*4+1+pos*2]=t;
}
void initWall() {
int subdiv=100; float back=1;
float dn=(2.0/3)/subdiv;
float nx=-1.0/3; float ny=-1.0/3;
float s=0; float t=0;
float dst=1.0/subdiv;
float x=-back; float y=-back;
float dp=(float)2*back/subdiv;
glEnable(GL_NORMALIZE);
geomVertices=new float[4*3*subdiv*subdiv];geomTexCoords=new float[4*2*subdiv*subdiv];
geomNormals=new float[4*3*subdiv*subdiv];geomVertexCount=4*subdiv*subdiv;
for (int i1=0;i1<subdiv;i1++) {
for (int i2=0;i2<subdiv;i2++) {
quad(subdiv,i1,i2,x,y,back,nx,ny,s,t,0);
quad(subdiv,i1,i2,x+dp,y,back,nx+dn,ny,s+dst,t,1);
quad(subdiv,i1,i2,x+dp,y+dp,back,nx+dn,ny+dn,s+dst,t+dst,2);
quad(subdiv,i1,i2,x,y+dp,back,nx,ny+dn,s,t+dst,3);
nx+=dn;x+=dp;s+=dst;
}
nx=-1.0/3;x=-back;s=0;
ny+=dn;y+=dp;t+=dst;
}
}
Ćwiczenie 2 (2)
Ćwiczenie 2 (2)
●
Przekopiuj poniższą procedurę:
void wall() {
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glVertexPointer(3,GL_FLOAT,0,geomVertices);
glNormalPointer(GL_FLOAT,0,geomNormals);
glTexCoordPointer(2,GL_FLOAT,0,geomTexCoords);
glDrawArrays(GL_QUADS,0,geomVertexCount);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}
●
Zastąp kod rysujący sześcian w displayFrame,
następującymi poleceniami:
glPushMatrix();
wall();
glRotatef(90,1,0,0);
wall();
glRotatef(90,1,0,0);
wall();
glRotatef(90,1,0,0);
wall();
glRotatef(90,1,0,0);
glRotatef(90,0,1,0);
wall();
glRotatef(180,0,1,0);
wall();
glPopMatrix();
Ćwiczenie 2 (cd)
Ćwiczenie 2 (cd)
Zobacz, czy teraz odbicia lustrzane są lepiej rysowane.
Dlaczego ?
Zaawansowane cieniowanie (13)
Zaawansowane cieniowanie (13)
●
Domyślnym ustawieniem światła jest białe światło
umieszczone w nieskończoności za obserwatorem.
●
W zależności od implementacji OpenGL można
używać różnej liczby świateł.
●
Maksymalną liczbę świateł określa stała
GL_MAX_LIGHTS.
●
Parametry świateł, takie jak: kolor, natężenie, pozycja i
typ można zmodyfikować za pomocą procedur glLightf
i glLightfv.
Zaawansowane cieniowanie (14)
Zaawansowane cieniowanie (14)
●
void glLightfv( GLenum light, GLenum pname,
const GLfloat *params );
–
light – stała określająca numer światła –
GL_LIGHTn.
–
pname – w celu określenia
charakterystyki światła pname przyjmuje
wartości GL_AMBIENT, GL_DIFFUSE
i GL_SPECULAR, wówczas params jest
wektorem czterech liczb (RGBA),
określających kolor danego rodzaju
światła.
Zaawansowane cieniowanie (15)
Zaawansowane cieniowanie (15)
●
Punktowe źródło światła:
–
Aby określić położenie światła, również
wykorzystywane jest polecenie glLightfv.
–
Jako pname należy tutaj podać GL_POSITION, a
jako param podajemy wektor 4 (x,y,z,w) liczb
określających położenie światła we
współrzędnych homogenicznych.
–
Współrzędne światła są przekształcane przez
macierz model - widok (są traktowane jak punkt w
przestrzeni obiektu) i zapamiętywane w
przestrzeni oka.
–
Jeżeli wartość w wektora położenia jest równa zero,
to pozostałe wartości są traktowane jako wektor
określający kierunek światła, a samo światło jest
umieszczane w nieskończoności wzdłuż wektora.
Zaawansowanie cieniowanie (16)
Zaawansowanie cieniowanie (16)
–
Jasność punktowego źródła światła może maleć wraz
z odległością.
–
Matematycznie jest to wyrażone przez współczynnik:
umieszczony przed jasnością światła w modelu oświetlenia.
–
Parametry k
c
, k
l
i k
q
można ustawić za pomocą
procedury glLightf podając jako pname odpowiednio
GL_CONSTANT_ATTENUATION,
GL_LINEAR_ATTENUATION i
GL_QUADRATIC_ATTENUATION, a odpowiednią
wartość jako param
Ćwiczenie
Ćwiczenie
Umieść w procedurze rysującej, przed rysowaniem
sześcianu z poprzedniego ćwiczenia następujące
instrukcje:
float
lightPos[]={0,0,-1,0};
glLightfv(GL_LIGHT0,GL_POSITION,lightPos);
Poobracaj trochę tym sześcianem i zaobserwuj różnicę w
zachowaniu oświetlenia w stosunku do poprzedniego
ćwiczenia. Zastanów się jak można wytłumaczyć to, że
światło się obraca wraz
z sześcianem, oraz to, że mimo tego odbicie lustrzane
pozostaje w miejscu.
Zaawansowane cieniowanie (17)
Zaawansowane cieniowanie (17)
●
Stożkowe źródło światła:
–
Punktowe źródło światła, ale światło nie rozchodzi się w
każdym kierunku jednakowo.
Zaawansowane cieniowanie (18)
Zaawansowane cieniowanie (18)
–
Kierunek stożka określa się za pomocą procedury
glLightfv podając jako pname
GL_SPOT_DIRECTION,
a wektor reprezentujący kierunek jako param (we
współrzędnych homogenicznych).
–
Połowę kąta rozwarcia stożka określa się za pomocą
procedury glLightf podając jako pname
GL_SPOT_CUTOFF, a jako param liczbę z zakresu
<0,90> lub 180 (co oznacza, światło punktowe)
–
Stopień skupienia światła w stożku określa się za
pomocą procedury glLightf podając jako pname
GL_SPOT_EXPONENT, a jako param liczbę z
zakresu <0,128>. Im większa wartość tym większe
skupienie, 0 oznacza równomierne rozmieszczenie
światła.
Ćwiczenie
Ćwiczenie
Usuń linijki kodu ustawiające pozycję światła dodane w poprzednim
ćwiczeniu. Ustaw właściwości materiału sześcianu tak, aby nie dawał on
odbić lustrzanych. Do części inicjującej, zaraz po załadowaniu do
macierzy model-widok macierzy widoku, dodaj kod ustawiający źródło
światła stożkowego GL_LIGHT0 w pozycji <0,0,6,1>, o kącie rozwarcia
20 stopni (GL_SPOT_CUTOFF – 10), skierowane w kierunku <0,0,-1,0>.
Uruchom program, zaobserwuj efekt działania oświetlenia. Następnie
dodaj linijkę ustawiającą skupienie światła na maksimum
(GL_SPOT_EXPONENT – 128) i znowu uruchom program.
Zaawansowane cieniowanie (19)
Zaawansowane cieniowanie (19)
●
Za pomocą procedur glLightModelf()
i glLightModelfv() można modyfikować pewne
dodatkowe parametry modelu oświetlenia. Tutaj
przeanalizujemy tylko jeden z nich.
●
Za pomocą glLightModelfv() można na przykład
zmienić rozproszone światło otoczenia (ambient).
–
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, params),
gdzie params, to wektor czterech liczb (RGBA)
określających kolor światła otoczenia.
Ćwiczenie
Ćwiczenie
Do wyniku poprzedniego ćwiczenia, w części inicjującej,
dodaj linijkę ustawiającą światło otoczenia na <1,1,1,1>
i zmodyfikuj materiał sześcianu tak, aby odbijał światło
otoczenia następująco <0.1,0.1,0.1,1>.