background image

 

 

OpenGL

Podstawy

background image

 

 

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ę.

background image

 

 

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 ?

background image

 

 

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.

background image

 

 

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.

background image

 

 

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.

background image

 

 

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

background image

 

 

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.

background image

 

 

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

background image

 

 

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);

background image

 

 

Ć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.

background image

 

 

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

background image

 

 

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.

background image

 

 

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.

background image

 

 

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.

background image

 

 

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.

background image

 

 

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.

background image

 

 

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.

background image

 

 

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)

background image

 

 

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

background image

 

 

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.

background image

 

 

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

background image

 

 

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 )

background image

 

 

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

background image

 

 

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 ;)

        .............

background image

 

 

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);

background image

 

 

Ć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;

background image

 

 

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>.

background image

 

 

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.

background image

 

 

Ć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.

background image

 

 

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.

background image

 

 

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);

background image

 

 

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();

}

background image

 

 

Ć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.

background image

 

 

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.

background image

 

 

Ć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 ?

background image

 

 

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.

background image

 

 

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

background image

 

 

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

background image

 

 

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

background image

 

 

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

background image

 

 

Ć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.

background image

 

 

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));

background image

 

 

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();

}

background image

 

 

Ć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.

background image

 

 

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().

background image

 

 

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.

background image

 

 

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();

background image

 

 

Ć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”:

background image

 

 

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.

background image

 

 

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 );

background image

 

 

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 );

background image

 

 

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 );

background image

 

 

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.

background image

 

 

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 ); 

background image

 

 

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 );

background image

 

 

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 ); 

background image

 

 

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 );

background image

 

 

Vertex Buffer Objects (6)

Vertex Buffer Objects (6)

Usuwanie bufora VBO z pamięci:

glDeleteBuffers(1,&geomVerticesId);

background image

 

 

Ć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.

background image

 

 

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

background image

 

 

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

background image

 

 

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()

background image

 

 

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

background image

 

 

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

background image

 

 

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)

background image

 

 

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.

background image

 

 

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

}

background image

 

 

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

background image

 

 

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.

background image

 

 

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.

background image

 

 

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)

background image

 

 

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 );

background image

 

 

Ćwiczenie

Ćwiczenie

Zmodyfikuj program z poprzednich ćwiczeń tak, aby 

rysował i animował teksturowany sześcian. Na 

potrzeby ćwiczenia wyłącz cieniowanie.

background image

 

 

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ść.

background image

 

 

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

background image

 

 

Zaawansowanie cieniowanie (3)

Zaawansowanie cieniowanie (3)

Normalną można uzyskać z następującego 

wyznacznika:

gdzie ij 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

background image

 

 

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);

background image

 

 

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)

background image

 

 

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 );

background image

 

 

Ć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ś ?

background image

 

 

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.

background image

 

 

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.

background image

 

 

Ćwiczenie

Ćwiczenie

Zmodyfikuj poprzednie ćwiczenie definiując teraz 

normalną dla każdego wierzchołka, jako średnią 

arytmetyczną normalnych sąsiadujących ścian. 

background image

 

 

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

background image

 

 

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. 

background image

 

 

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.

background image

 

 

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)

background image

 

 

Ć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.

background image

 

 

Ć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;

}

}

background image

 

 

Ć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();

background image

 

 

Ćwiczenie 2 (cd)

Ćwiczenie 2 (cd)

Zobacz, czy teraz odbicia lustrzane są lepiej rysowane. 

Dlaczego ?

background image

 

 

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.

background image

 

 

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.

background image

 

 

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.

background image

 

 

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

background image

 

 

Ć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.

background image

 

 

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.

background image

 

 

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.

background image

 

 

Ć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.

background image

 

 

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.

background image

 

 

Ć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>.


Document Outline