'
SPRAWOZDANIE
Z LABORATORIUM
GRAFIKI KOMPUTEROWEJ
Temat: Modelowanie oświetlenia.
wykonał: Piotr Korlaga I0X4S1
data wykonania ćwiczenia: 15.12.2011r.
1. Treść zadania.
Zadanie 16
Wykorzystując biblioteki OpenGL i GLUT napisać program przedstawiający
perspektywiczny obraz obiektu o następujących parametrach:
1.
Typ obiektu: bryła z ćwicz. 2 o zmiennej parzystej liczbie podziałów pionowych i
poziomych,
2.
Właściwości materiału nr 1: żółty emitujący (widziany w białym świetle),
3.
Właściwości materiału nr 2: zielony matowy (widziany w białym świetle),
4.
Sposób przyporządkowania materiałów do obiektu zgodnie ze wzorem: pasy
pionowe z uwzględnieniem podziałów pionowych.
Obiekt należy oświetlić dwoma źródłami światła o następujących parametrach:
Źródło nr 1:
−
typ: reflektor (ang. spot),
−
kolor: fioletowy,
−
natężenie: 0.9,
−
kąt odcięcia: 45
o
,
−
położenie: zmienne po orbicie kołowej o środku w punkcie S(0,0,0) z możliwością
interaktywnej zmiany następujących parametrów:
o
promienia orbity,
o
prędkości kątowej (3 różne prędkości),
o
kąta nachylenia orbity do osi OX,
−
kierunek świecenia: na obiekt.
Źródło nr 2:
−
typ: kierunkowe,
−
kolor: zielony,
−
natężenie: 0.7,
−
położenie: stałe w punkcie P(-10,-5, 10) układu współrzędnych sceny.
−
kierunek świecenia: na obiekt.
Program powinien umożliwiać:
a) interaktywne, niezależne włączanie i wyłączanie źródeł światła;
b) interaktywną zmianę liczby podziałów pionowych i poziomych bryły;
c) interaktywną zmianę wielkości bryły;
d) interaktywną zmianę położenia obserwatora poprzez podanie następujących parametrów:
−
odległości obserwatora od środka układu współrzędnych sceny;
−
kąta obrotu wokół osi OY w zakresie [0
o
, 360
o
] z krokiem 1
o
.
Oświetlony obiekt powinien zawsze znajdować się w centralnej części okna
2. Wstęp
W celu rozwiązania tego zadania należy zdefiniować macierz parametrów dla każdego
materiału, a następnie zaimplementować przydział kolorów zgodnie z punktem 4. z treści zadania.
Kolejnym krokiem jest zdefiniowanie macierzy dla parametrów światła i zaimplementowanie
możliwości ich niezależnego włączania, przy czym dla źródła nr 2 należy dokonać odpowiedniej
animacji aby wprawić źródło w ruch. Ponadto zależy przewidzieć obsługę klawiatury.
3. Rozwiązanie
3.1 Zdefiniowanie parametrów materiału i przydział materiałów pasami pionowymi.
3.1.1
Implementacja
Deklaracja macierzy:
GLfloat material_zolty[5][4]=
{
{1,1,0,1},
{1,1,0,1},
{1,0,1,1},
{1,0,0,0},
{0.3,0.3,0.0,1}
};
GLfloat material_zielony[5][4]=
{
{0,1,0,1},
{0,1,0,1},
{1,0,1,1},
{1,0,0,0},
{0,0,0,0}
};
Funkcje ładujące macierze z parametrami kolorów:
void
Material_zolty()
{
glMaterialfv(GL_FRONT, GL_AMBIENT, material_zolty[0]);
glMaterialfv(GL_FRONT, GL_DIFFUSE, material_zolty[1]);
glMaterialfv(GL_FRONT, GL_SPECULAR, material_zolty[2]);
glMaterialfv(GL_FRONT, GL_SHININESS, material_zolty[3]);
glMaterialfv(GL_FRONT, GL_EMISSION, material_zolty[4]);
}
void
Material_zielony()
{
glMaterialfv(GL_FRONT, GL_AMBIENT, material_zielony[0]);
glMaterialfv(GL_FRONT, GL_DIFFUSE, material_zielony[1]);
glMaterialfv(GL_FRONT, GL_SPECULAR, material_zielony[2]);
glMaterialfv(GL_FRONT, GL_SHININESS, material_zielony[3]);
glMaterialfv(GL_FRONT, GL_EMISSION, material_zielony[4]);
}
Funkcja rysująca stożek – przydział kolorów
void
Rysuj(
double
r)
{
for
(j=0,k=0;(j+4.0/N2<=WYSOKOSC);j+=4.0/N2,k+=4.0/N2)
{
glBegin(GL_QUAD_STRIP);
for
(i=0; i*dAlfa<=180.0; i++)
{
if
((
int
)i % 2== 0) Material_zolty();
else
Material_zielony();
//TUTAJ ŚCIANA BOCZNA
}
glEnd();
}
for
(l=0,k=-r;(l+4.0/N2<=WYSOKOSC);l+=4.0/N2,k-=4.0/N2)
{
glBegin(GL_QUAD_STRIP);
for
(a=0,i=k,j=k-4.0/N2;i<=-k &&j<=-k+4.0/N2;i+=dAlfa/100*
(-k),j+=dAlfa/100*(- k+4.0/N2),zz++)
{
if
((
int
)a% 2 == 0) Material_zolty();
else
Material_zielony();
//TUTAJ ŚCIANA BOCZNA
glEnd();
}
glBegin(GL_QUAD_STRIP);
for
(i=0; i*dAlfa<=180.0; i++)
{
if
((
int
)i% 2 == 0) Material_zolty();
else
Material_zielony();
//TUTAJ PODSTAWA DOLNA
}
glEnd();
glBegin(GL_QUAD_STRIP);
for
(i=0; i*dAlfa<=180.0; i++)
{
if
((
int
)i% 2 == 0) Material_zolty();
else
Material_zielony();
//TUTAJ PODSTAWA GORNA
glEnd();
}
3.1.2
Opis
Powyższe tablice składają się z 5 wierszy, przy czym każdy wiersz określa odpowiedni
parametr o wartości odpowiadającej składowym RGBA, i tak:
I ambient – współczynnik odbicia światła otoczenia,
II diffuse – współczynnik odbicia światła rozproszonego,
III specular – współczynnik odbicia światła lustrzanego,
IV shinines – współczynnik połysku,
V emission – współczynnik światła emitowanego.
Kolor żółty i zielony został ustwione w parametrach ambient i diffuse. Wartość
110 odpowiada kolorowi żółtemu, a wartość 010 kolorowi zielonemu w RGB.Dodatkowo
parametrowi specular przypsałem wartość 1,0,1,1 zgodnie z kolorem źródła światła, aby
otrzymać realistyczny efekt. Współczynnik połysku zasugerowany w instrukcji okazał się
jednak moim zdaniem za duży, ustawiłem go na 1 i nawet taka mała wartość daje ciekawy
efekt. Aby uzyskać kolor żółty emitujący ustawiłem wartość emission na (0.3,0.3,0.0,1)
odpowiada to kolorowi żółtemu, większe współczynniki dają już mniej realistyczny efekt.
Następnie stworzyłem funkcję, która ustawia dla każdego parametru odpowiadający jej
wiersz macierzy za pomocą funkcji
glMaterialfv(GL_FRONT, GL_PARAMETR,material[nr
wiersza]).
Przydział kolorów zrealizowałem za pomocą instrukcji warunkowej if w pętli
rysującej każdą scianę. W tym celu ustawiłem dodatkowy licznik a lub i. Gdy licznik jest
parzysty przydzielany jest kolor żółty, w przeciwnym wypadku zielony.
3.1.3 Efekt
3.2
Ustawienie wektorów normalnych.
3.2.1 Implementacja
void
Rysuj(
double
r)
{
float
Rgornej=r+4*r;
glColor3f(0.0,1.0,0.0);
for
(j=0,k=0;(j+4.0/N2<=WYSOKOSC);j+=4.0/N2,k+=4.0/N2)
{
glBegin(GL_QUAD_STRIP);
for
(i=0; i*dAlfa<=180.0; i++)
{
if
((
int
)i % 2== 0) Material_zolty();
else
Material_zielony();
glNormal3f((r+k)*cos(DEG2RAD(i*dAlfa)), -(r+k),
(r+k)*sin(DEG2RAD(i*dAlfa)));
glVertex3f((r+k)*cos(DEG2RAD(i*dAlfa)), j, (r+k)*sin(DEG2RAD(i*dAlfa)));
glVertex3f((r+k+4.0/N2)*cos(DEG2RAD(i*dAlfa)), j+4.0/N2,
(r+k+4.0/N2)*sin(DEG2RAD(i*dAlfa)));
}
glEnd();
}
for
(l=0,k=-r;(l+4.0/N2<=WYSOKOSC);l+=4.0/N2,k-=4.0/N2)
{
glBegin(GL_QUAD_STRIP);
glNormal3f(0,0,-1);
for
(zz=0,i=k,j=k-4.0/N2;i<=-k &&j<=-k+4.0/N2;i+=dAlfa/100*(-k),j+=dAlfa/100*(-
k+4.0/N2),zz++)
{
if
((
int
)zz% 2 == 0) Material_zolty();
else
Material_zielony();
glVertex3f(i*cos(DEG2RAD(180)), l, i*sin(DEG2RAD(180)));
glVertex3f(j*cos(DEG2RAD(180)), l+4.0/N2, j*sin(DEG2RAD(180)));
}
glVertex3f(-k*cos(DEG2RAD(180)), l, -k*sin(DEG2RAD(180)));
glVertex3f((-k+4.0/N2)*cos(DEG2RAD(180)), l+4.0/N2, (-
k+4.0/N2)*sin(DEG2RAD(180)));
glEnd();
}
glBegin(GL_QUAD_STRIP);
glNormal3f(0,1,0);
for
(i=0; i*dAlfa<=180.0; i++)
{
if
((
int
)i% 2 == 0) Material_zolty();
else
Material_zielony();
glVertex3f(0,l,0);
glVertex3f(-k*cos(DEG2RAD(i*dAlfa)),l,-k*sin(DEG2RAD(i*dAlfa)));
}
glEnd();
glBegin(GL_QUAD_STRIP);
glNormal3f(0,-1,0);
for
(i=0; i*dAlfa<=180.0; i++)
{
if
((
int
)i% 2 == 0) Material_zolty();
else
Material_zielony();
glVertex3f(0,0,0);
glVertex3f(r*cos(DEG2RAD(i*dAlfa)), 0, r*sin(DEG2RAD(i*dAlfa)));
}
glEnd();
}
3.2.2
Opis
Z wyznaczeniem wektorów dla podstawy górnej i dolnej oraz dla ściany bocznej tylniej nie
ma problemów, bowiem są wektor normalny to wektor prostopadły do ściany, i tak:
Podstawa dolna N=(0,-1,0)
Podstawa górna N=(0,1,0)
Ściana boczna tylna N=(0,0,-1)
Do wyznaczenia wektorów ściany bocznej przedniej zbudujmy równanie stożka:
Następnie wyznaczamy iloczyn wektorowy pochodnych z równania parametrycznego:
Jako że nasz stożek jest odwrócony otrzymujemy wektor normalny o współrzęnych
odwróconych, czyli:
Funkcja
glEnable(GL_NORMALIZE)
odpowiadaja za normalizację wektora.
3.3 Źródło numer 1
3.3.1 Implementacja
Deklaracja macierzy:
GLfloat swiatlo1[10][4]= {
{0.0, 0.0, 0.0, 0.0},
{1.0, 0.0, 1.0, 0.0},
{1.0, 0.0, 1.0, 0.0},
{0.0, 0.0, 0.0, 1.0},
{0.0, 0.0, 1.0, 0.0},
{0.0, 0.0, 0.0, 0.0},
{45.0, 0.0, 0.0, 0.0},
{0.9, 0.0, 0.0, 0.0},
{0.0, 0.0, 0.0, 0.0},
{0.0, 0.0, 0.0, 0.0}};
Funkcje ładujące macierze z parametrami oświetlenia:
void
WlaczOswietlenie(
void
)
{
glEnable(GL_LIGHTING);
if
(Zrodlo1==1){
glEnable(GL_LIGHT1);
glLightfv(GL_LIGHT1, GL_AMBIENT, swiatlo1[0]);
glLightfv(GL_LIGHT1, GL_DIFFUSE, swiatlo1[1]);
glLightfv(GL_LIGHT1, GL_SPECULAR, swiatlo1[2]);
glLightfv(GL_LIGHT1, GL_POSITION, swiatlo1[3]);
glLightfv(GL_LIGHT1, GL_SPOT_DIRECTION, swiatlo1[4]);
glLightf(GL_LIGHT1, GL_SPOT_EXPONENT, swiatlo1[5][0]);
glLightf(GL_LIGHT1, GL_SPOT_CUTOFF, swiatlo1[6][0]);
glLightf(GL_LIGHT1, GL_CONSTANT_ATTENUATION, swiatlo1[7][0]);
glLightf(GL_LIGHT1, GL_LINEAR_ATTENUATION, swiatlo1[8][0]);
glLightf(GL_LIGHT1, GL_QUADRATIC_ATTENUATION, swiatlo1[9][0]);
}
else
glDisable(GL_LIGHT1);
}
Funkcja rysująca stożek – reflektor
void
Rysuj(
double
r)
{
swiatlo1[3][0]=PromienReflektora*(cos(alfa));
swiatlo1[3][1]=nachyelenie;
swiatlo1[3][2]=PromienReflektora*(sin(alfa));
swiatlo1[4][0]=-(PromienReflektora*cos(alfa));
swiatlo1[4][1]=-nachylenie;
swiatlo1[4][2]=-(PromienReflektora*sin(alfa));
glPushMatrix();
glTranslatef(PromienReflektora*(cos(alfa)),nachylenie,PromienReflektora*(
sin(alfa)));
glutSolidSphere(0.5,10,10);
glPopMatrix();
alfa+=PredkoscReflektora;
(…)
}
3.3.2 Opis
Implementację pierwszego źródła światła rozpoczynamy od deklaracji macierzy z
parametrami źródła światła i tak:
I ambient – świtało otoczenia ustawione na 0,0,0 aby zapewnić efekt realistycznego
reflektora
II diffuse – światło rozproszenia ustawione na 1,0,1 oznacza kolor fioletowy
III specular - światło lustrzane ustawione na 1,0,1 oznacza kolor fioletowy
IV possition – pozycja reflektora w deklaracji nie ma znaczenia gdyż jest zmienna
V direction – kierunek świecenia w deklaracji nie ma znaczenia gdyż jest zmienny
VI exponent – domyślnie brak tlumienia
VII spot_cutoff – kąt odcięcia 45 stopni
VIII constant attenuation – ustawiłem tutaj stałe natężenie na 0.9
IX linear attenuation – wartość domyślna
X linear attenuation – wartość domyślna
W funkcji WlaczOswietlenie() ładowane są parametry źródła światła za pomocą funkcji
glLightfv(GL_LIGHT1, PARAMETR, swiatlo1[nr wiersza])
. Posłużyłem się tutaj również
zmienną boolowską Zrodlo1, która ustawiamy na 1 gdy chcemy, aby światło było włączone
lub na 0, gdy chcemy, aby było wyłączone.
W funkcji rysującej na początku ustawiane są parametry światła, które nie zostały ustawione
w deklaracji czyli położenie i kierunek świecenia przeciwny do położenia. Za pomocą
funkcji
glutSolidSphere(0.5,10,10);
zobrazowany jest reflektor poruszający się po orbicie
zależnej od zmiennej
nachylenie
i
PromienReflektora.
Animacja reflektora polega na
zmianie kąta
alfa(
w radianach) w każdej klatce animacji o zmienną
PredkoscReflektora
.
3.3.3
Efekt
b) Zmiana promienia orbity oraz kąta nachylenia reflektora:
3.4 Źródło numer 2
3.4.1 Implementacja
Deklaracja macierzy:
GLfloat swiatlo2[10][4]= {
{0.0, 1.0, 0.0, 0.0},
// [0] otoczenie
{0.0, 1.0, 0.0, 0.0},
// [1] rozproszenie
{0.0, 1.0, 0.0, 0.0},
// [2] lustrzane
{-10.0, -5.0, 10.0, 1.0},
// [3] polozenie
{0.0, 0.0, 1.0, 0.0},
// [4] kierunek swiecenia
{0.0, 0.0, 0.0, 0.0},
// [5] tlumienie katowe swiatla
{180.0, 0.0, 0.0, 0.0},
// [6] kat odciecia swiatla
{0.0, 0.0, 0.0, 0.0},
// [7] stale tlumienie
{0.7, 0.0, 0.0, 0.0},
// [8] tlumienie liniowe
{0.0, 0.0, 0.0, 0.0}};
// [9] tlumienie kwadratowe;
Funkcje ładujące macierze z parametrami oświetlenia:
void
WlaczOswietlenie(
void
)
{
glEnable(GL_LIGHTING);
if
(Zrodlo2==1){
glEnable(GL_LIGHT0);
glLightfv(GL_LIGHT0, GL_AMBIENT, swiatlo2[0]);
glLightfv(GL_LIGHT0, GL_DIFFUSE, swiatlo2[1]);
glLightfv(GL_LIGHT0, GL_SPECULAR, swiatlo2[2]);
glLightfv(GL_LIGHT0, GL_POSITION, swiatlo2[3]);
glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, swiatlo2[4]);
glLightf(GL_LIGHT0, GL_SPOT_EXPONENT, swiatlo2[5][0]);
glLightf(GL_LIGHT0, GL_SPOT_CUTOFF, swiatlo2[6][0]);
glLightf(GL_LIGHT0, GL_CONSTANT_ATTENUATION, swiatlo2[7][0]);
glLightf(GL_LIGHT0, GL_LINEAR_ATTENUATION, swiatlo2[8][0]);
glLightf(GL_LIGHT0, GL_QUADRATIC_ATTENUATION, swiatlo2[9][0]);
}
else
glDisable(GL_LIGHT0);
}
3.4.2 Opis
Implementację drugiego źródła światła rozpoczynamy od deklaracji macierzy z
parametrami źródła światła i tak:
I ambient –światło otoczenia ustawione na 0,1,0 oznacza kolor zielony
II diffuse – światło rozproszenia ustawione na 0,1,0 oznacza kolor zielony
III specular - światło lustrzane ustawione na 0,1,0 oznacza kolor zielony
IV possition – pozycja ustawiona jest zgodnie z treścią zadania na (-5,-10,10)
V direction – kierunek świecenia na obiekt
VI exponent – domyślnie brak tlumienia
VII spot_cutoff – kąt odcięcia ma wartość 180 oznacza, że światło rozchodzi się
równomiernie
VIII constant attenuation – wartość domyślna
IX linear attenuation – ustawiłem natężenie na 0,7
X linear attenuation – wartość domyślna
W funkcji WlaczOswietlenie() ładowane są parametry źródła światła za pomocą
funkcji
glLightfv(GL_LIGHT1, PARAMETR, swiatlo1[nr wiersza])
. Posłużyłem się tutaj
również zmienną boolowską Zrodlo2, która ustawiamy na 1 gdy chcemy, aby światło
było włączone lub na 0, gdy chcemy, aby było wyłączone
3.4.3 Efekt
Źródło nr 2:
Źródło nr 2 + Źródło nr 1:
4. Obsługa klawiatury.
4.1 Implementacja
void
ObslugaKlawiatury(
unsigned
char
klawisz)
{
if
(klawisz ==
'['
) OBSERWATORODLEGLOSC -= 1;
else
if
(klawisz ==
']'
) OBSERWATORODLEGLOSC +=1;
else
if
(klawisz ==
'-'
&& N >= 8)
N-=2;
else
if
(klawisz ==
'+'
&& N <= 128)
N+=2;
else
if
(klawisz ==
'*'
&& N2>= 4)
N2 -=2;
else
if
(klawisz ==
'9'
&& N2 <= 64) N2 +=2;
else
if
(klawisz ==
'1'
) Zrodlo1 = 1;
else
if
(klawisz ==
'!'
) Zrodlo1 = 0;
else
if
(klawisz ==
'2'
) Zrodlo2= 1;
else
if
(klawisz ==
'@'
) Zrodlo2 = 0;
else
if
(klawisz ==
'd'
) OBSERWATOROBROTY +=1.0;
else
if
(klawisz ==
'D'
) OBSERWATOROBROTY -=1.0;
else
if
(klawisz ==
'r'
&& PromienReflektora> 3)
ref_r -=1.0;
else
if
(klawisz ==
'R'
&& PromienReflektora < 15) ref_r +=1.0;
else
if
(klawisz ==
'f'
) nachylenie-=1.0;
else
if
(klawisz ==
'F'
) nachylenie+=1.0;
else
if
(klawisz ==
'4'
) PredkoscReflektora=0.001;
else
if
(klawisz ==
'5'
) PredkoscReflektora=0.02;
else
if
(klawisz ==
'6'
) PredkoscReflektora =0.1;
else
if
(klawisz ==
'q'
) r -=0.1;
else
if
(klawisz ==
'Q'
) r +=0.1;
else
if
(klawisz ==
'w'
&& WYSOKOSC > 0.2)
WYSOKOSC -=0.1;
else
if
(klawisz ==
'W'
) WYSOKOSC +=0.1;
else
if
(klawisz == 27)
exit(0);
}
4.2 Opis
Dzięki powyższej implementacju użytkownik ma możliwość wpływania na parametery
znajdujące się w funkcji wyświetlającej jedną klatkę animacji. Ogólnie rzecz biorąc obsługa
klawiatury polega na tym, że po wciśnięciu klawisza, który porównywany jest z wartością w
warunku if następuje inkrementacja bądź dekrementacja (z odpowiednim krokiem wynikającym
z treści zadania) wartości parametru, który odpowiada danemu klawiszowi. Następnie każda
zmienna, która została zmodyfikowana w tej funkcji jest również widzialna w funkcji
WyswietlObraz, gdyż jest to zmienna globalna.
4.3
Efekty
a) Zmiana liczby podziałów
b) Zmiana wielkości bryły – zwiększenie promienia i zmniejszenie wysokości
c)Zmiana odległości obserwatora
d)Obrót wokół osi Y
5. Wnioski końcowe.
Ćwiczenie to pozwoliło mi rozwinąć swoje umiejętności w kwestii modelowania
oświetlenia. Nie było to zadanie łatwe, ponieważ musiałem poświęcić dużo czasu na
zgłębienie wiedzy teoretycznej dotyczącej modelowania oświetlenia przy pomocy biblioteki
OpenGL. Ponadto,jak się później okazało, sama wiedza teoretyczna nie wystarczy i potrzebne
jest tutaj spore doświadczenie, gdyż często parametry oświetlenia i materiału trzeba dobierać
metodą prób i błędów, a ocena realistyczności danego efektu jest dość subiektywna. W trakcie
wykonywania zadania na laboratoriach z powodu problemów ze sprzętem(nieprawidłowe
wyświetlanie) ciężko było zrealizować zadanie zgodnie z instrukcją, gdyż nie mogłem
sprawdzić poprawności mojego rozwiązania. Dało mi to do myślenia, że pomimo
„przenośności” biblioteki OpenGL nie wszystkie elementy działają tak samo na różnych
maszynach np. szybkość animacji jest zależna od szybkości maszyny, a problemy z
wyświetlaniem mogą być spowodowane nieaktualnymi sterownikami karty graficznej.