WOJSKOWA AKADEMIA TECHNICZNA
IM. JAROSŁAWA DĄBROWSKIEGO
LABORATORIUM GRAFIKI KOMPUTEROWEJ
SPRAWOZDANIE Z ĆWICZENIA LABORATORYJNEGO NR 4
Prowadzący : dr inż. Marek Salamon
Sprawozdanie wykonał : Loay Achmasiewicz
Grupa : I7X1S1
Data wykonania : 03.02.2009
Zestaw zadania : 20
Treść zadania
W ramach ćwiczenia laboratoryjnego otrzymałem do wykonania zestaw 2:
Wykorzystując biblioteki OpenGL i GLUT napisać program przedstawiający perspektywiczny obraz obiektu o następujących parametrach:
Typ obiektu: walec o zmiennej podzielnej przez 3 liczbie podziałów pionowych i poziomych,
Właściwości materiału 1: fioletowy błyszczący (widziany w białym świetle),
Właściwości materiału 2: żółty matowy (widziany w białym świetle),
Właściwości materiału 3: zielony emitujący (widziany w białym świetle),
Sposób przyporządkowywania 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: biały ,
natężenie: 1,
kąt odcięcia: 300,
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:
promienia orbity,
prędkości kątowej (3 różne prędkości),
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, 10, 10) układu współrzędnych obserwatora
kierunek świecenia: na obiekt.
Program powinien umożliwiać:
interaktywne, niezależne włączanie i wyłączanie źródeł światła;
interaktywną zmianę liczby podziałów pionowych i poziomych bryły;
interaktywną zmianę wielkości bryły;
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 [00, 3600] z krokiem 10.
Oświetlony obiekt powinien zawsze znajdować się w centralnej części okna.
Rozwiązanie Zadania.
Rozwiązanie zadania rozpocząłem od zdefiniowania parametrów oświetlenia oraz materiałów.
// Materiał fioletowy blyszczacy
const GLfloat ambient_m1[4] = { 1, 0, 1, 1 };
const GLfloat diffuse_m1[4] = { 0.4, 0, 0.4, 1 };
const GLfloat specular_m1[4] = { 1, 0, 1, 1 };
const GLfloat emission_m1[4] = { 0, 0, 0, 0 };
// material matowy zolty
const GLfloat ambient_m2[4] = { 0.8, 0.8, 0, 1 };
const GLfloat diffuse_m2[4] = { 0.8, 0.8, 0, 1 };
const GLfloat specular_m2[4] = { 0, 0, 0, 1 };
const GLfloat emission_m2[4] = { 0, 0, 0, 0 };
// material zielony emitujacy
const GLfloat ambient_m3[4] = { 0, 0, 0, 1 };
const GLfloat diffuse_m3[4] = { 0, 0, 0, 1 };
const GLfloat specular_m3[4] = { 0, 0, 0, 1 };
const GLfloat emission_m3[4] = { 0, 0.6, 0, 1 };
//reflektor
const GLfloat ambient_s1[4] = { 1, 1, 1, 1 }; // bialy
const GLfloat diffuse_s1[4] = { 1, 1, 1, 1 };
GLfloat specular_s1[4] = { 1, 1, 1, 1 };
GLfloat position_s1[4] = { 0, 0, 0, 1 };
GLfloat direction_s1[4] = { -10, 0, 0 };
// swiatlo kierunkowe
const GLfloat ambient_s2[4] = {0, 0.7, 0, 1};
const GLfloat diffuse_s2[4] = {0, 0.7, 0, 1};
GLfloat specular_s2[4] = { 1, 1, 1, 1 };
GLfloat position_s2[4] = { 10, 10, 10, 0 };
Składowe parametru ambient określa w jakim stopniu obiekt odbija światło otoczenia. Diffuse określa jaka ilość światła padającego na obiekt ulega na jego powierzchni rozproszeniu, natomiast specular - określa jaka ilość światła odbija się w sposób lustrzany. Aby uzyskać opowiedni rezultat , w materiale pierwszym należało ustawić małe wartości diffuse, a większe specular - dzięki temu otrzymujemy błyszczący materiał (dla drugiego materiału odwotnie).
Trzeci materiał tylko emituje więc ustawiony zostaje parametr emission - emitacja światła przez obiekt (domyślnie parametr ten jest wyzerowany ).
Odnośnie parametrów światła to position służy do określenia pozycji światła w przestrzeni, a direction to kierunek (wektor) w jakim świeci światło.
Następnie napisane zostały funkcje tworzące materiały i światło :
void Material1() {
glMaterialfv(GL_FRONT, GL_AMBIENT, ambient_m1);
glMaterialfv(GL_FRONT, GL_DIFFUSE, diffuse_m1);
glMaterialfv(GL_FRONT, GL_SPECULAR, specular_m1);
glMaterialf(GL_FRONT, GL_SHININESS, 128);
glMaterialfv(GL_FRONT, GL_EMISSION, emission_m1);
}
void Material2() {
glMaterialfv(GL_FRONT, GL_AMBIENT, ambient_m2);
glMaterialfv(GL_FRONT, GL_DIFFUSE, diffuse_m2);
glMaterialfv(GL_FRONT, GL_SPECULAR, specular_m2);
glMaterialf(GL_FRONT, GL_SHININESS, 0);
glMaterialfv(GL_FRONT, GL_EMISSION, emission_m2);
}
void Material3() {
glMaterialfv(GL_FRONT, GL_AMBIENT, ambient_m3);
glMaterialfv(GL_FRONT, GL_DIFFUSE, diffuse_m3);
glMaterialfv(GL_FRONT, GL_SPECULAR, specular_m3);
glMaterialf(GL_FRONT, GL_SHININESS, 10);
glMaterialfv(GL_FRONT, GL_EMISSION, emission_m3);
}
Odpowiednio tworzące materiał 1/2/3 . W materiale pierwszym (błyszczący fiolet) parametr SHININESS ma wartosc maksymalna . Dla materiału matowego (w tym przypadku materiał nr2) powinien mieć wartość 0. Dla materiału emitującego , parametr ten jest nieistotny.
void Swiatlo1() {
glPushMatrix();
glEnable(GL_LIGHTING);
glRotatef(kat_y, 1, 0, 0);
glRotatef(kat_x, 0, 1, 0);
glRotatef(kat_z, 0, 0, 1);
glTranslatef(R_Light0, 0, 0);
glPushMatrix();
glDisable(GL_LIGHTING);
glColor3f(1, 1, 1);
glutSolidSphere(0.25, 20, 20);
glEnable(GL_LIGHTING);
glPopMatrix();
glLightfv(GL_LIGHT1, GL_DIFFUSE, diffuse_s1);
glLightfv(GL_LIGHT1, GL_SPECULAR, specular_s1);
glLightfv(GL_LIGHT1, GL_POSITION, position_s1);
glLightf(GL_LIGHT1, GL_SPOT_CUTOFF, 30);
glLightfv(GL_LIGHT1, GL_SPOT_DIRECTION, direction_s1);
glPopMatrix();
}
void Swiatlo2() {
glPushMatrix();
glTranslatef(10, 10, 10);
glPushMatrix();
glDisable(GL_LIGHTING);
glColor3f(0, 1, 0);
glutWireSphere(0.25, 20, 20);
glEnable(GL_LIGHTING);
glPopMatrix();
glLightfv(GL_LIGHT2, GL_DIFFUSE, diffuse_s2);
glLightfv(GL_LIGHT2, GL_SPECULAR, specular_s2);
glLightfv(GL_LIGHT2, GL_POSITION, position_s2);
glPopMatrix();
}
Parametry źródeł światła mogą być modyfikowane niezależnie od innych źródeł. Język OpenGL pozwala na zdefiniowanie do 8 źródeł światła w jednej scenie. Dla każdego źródła jest domyślna nazwa GL_LIGHTn ,gdzie n od 0 do 7. Oczywiście aby zdefiniować źródło GL_LIGHT1 musi już istnieć źródło GL_LIGHT0. Aby włączyć źródło światła trzeba się posłużyć funkcja glEnable(GL_LIGHTn) , odpowiednio do wyłączenia źródła światła służy funkcja glDisable(GL_LIGHTn) (początkowo wszystkie źródła są wyłączone).
W funkcji Swiatlo1 widać , że źródło światła reprezentuje biała kula . Odpowiednio w drugiej funkcji reprezentantem jest zielona kula (nie jest to potrzebne gdyż położenie źródła jest stałe ), w przypadku oddalenia obserwatora można zauważyć owe źródło.
Funkcja tworząca walec :
void RysujWalec(double h, double r, int nv, int nh)
{
double dH, dAlfa;
int i, j;
// Wyznaczenie kata wyznaczajacego pojedynczy wycinek pionowy
dAlfa = 360.0L/(double)nh;
// Wyznaczenie wysokosci pojedynczego wycinka poziomego
dH = h/(double)nv;
// Wyznaczanie wierzcholkow i wektorow normalnych powierzchni bocznych
for (i = 0; floor((i+1)*dH*1.0E10) <= floor(h*1.0E10); i++)
{
glBegin(GL_TRIANGLE_STRIP);
glNormal3f(0.0, 0.0, 1.0);
glVertex3f(0.0, (i + 1)*dH, r);
glVertex3f(0.0, i*dH, r);
for (j = 1; j*dAlfa <= 360.0L + dAlfa; j++)
{
if (j%3 == 0) {
Material1();
} else if (j%3 ==1){
Material3();
} else {
Material2();
}
glNormal3f(sin(DEG2RAD(j*dAlfa)), 0.0, cos(DEG2RAD(j*dAlfa)));
glVertex3f(r*sin(DEG2RAD(j*dAlfa)), (i + 1)*dH, r*cos(DEG2RAD(j*dAlfa)));
glVertex3f(r*sin(DEG2RAD(j*dAlfa)), i*dH, r*cos(DEG2RAD(j*dAlfa)));
}
glEnd();
}
// Wyznaczenie wierzcholkow i wektorow normalnych dolnej podstawy
glBegin(GL_TRIANGLE_FAN);
glNormal3f(0.0, -1.0, 0.0);
glVertex3f(0.0, 0.0, 0.0);
for (i = 0; i * dAlfa <= 360.0L + dAlfa; i++)
{ if (i%3 == 1) {
Material3();
} else if (i%3 ==0){
Material1();
} else
Material2();
glVertex3f(r*sin(DEG2RAD(i*dAlfa)), 0.0, r*cos(DEG2RAD(i*dAlfa)));
}glEnd();
// Wyznaczenie wierzcholkow i wektorow normalnych gornej podstawy
glBegin(GL_TRIANGLE_FAN);
glNormal3f(0.0, 1.0, 0.0);
glVertex3f(0.0, h, 0.0);
for (i = 0; i * dAlfa <= 360.0L + dAlfa; i++){
if (i%3 == 1) {
Material3();
} else if (i%3 ==0){
Material1();
} else
Material2();
glVertex3f(r*sin(DEG2RAD(i*dAlfa)), h, r*cos(DEG2RAD(i*dAlfa)));
}
glEnd();
Swiatlo1();
Swiatlo2();
}
Funkcja rysuje walec o zadanej wysokości, promieniu oraz liczbie podziałów pionowych i poziomych. Tryb modelowania powierzchni bocznej to GL_TRIANGLE_STRIP , a podstaw to GL_TRIANGLE_FAN. Dodatkowo w zależności wyniku j%3 oraz i%3 używane są odpowiednie materiały. Jak widać funkcja powyżej również konstruuje wektory normalne , dzięki czemu oświetlenie jest generowane prawidłowo. Wektor normalny danej powierzchni to wektor do niej prostopadły. Określa on jej orientację w przestrzeni, w szczególności względem źródła światła i obserwatora. Na jego podstawie można wyznaczyć ilość światła jaka dociera do powierzchni i ilość, która w kierunku obserwatora jest od niej odbijana. W OpenGL obliczenia związane z oświetleniem wkonywane są dla wierzchołków.
Wyniki działania programu.
1 2
3 4
1 - Walec bez oświetlenia.
2 - Walec oświetlany reflektorem.
3 - Walec oświetlany światłem kierunkowym (zielonym).
4 - Walec oświetlany reflektorem oraz światłem kierunkowym.
Obsługa klawiatury.
Funkcja odpowiedzialna za obsługę klawiatury :
void ObslugaKlawiatury(unsigned char klawisz, int x, int y)
{
switch(klawisz)
{
case '-': OBSERWATOR_ODLEGLOSC++;
break;
case '+': OBSERWATOR_ODLEGLOSC--;
break;
case 'v': OBSERWATOR_OBROT_Y++;
break;
case 'b': OBSERWATOR_OBROT_Y--;
break;
case '4': R_Light0+=0.1;
break;
case '$': R_Light0-=0.1;
break;
case '8':
lPionowych = (lPionowych == LPION_MAX)? LPION_MAX : lPionowych + 3;
break;
case '*':
lPionowych = (lPionowych == LPION_MIN)? LPION_MIN : lPionowych - 3;
break;
case '9':
lPoziomych = (lPoziomych == LPOZ_MAX)? LPOZ_MAX : lPoziomych + 3;
break;
case '(':
lPoziomych = (lPoziomych == LPOZ_MIN)? LPOZ_MIN : lPoziomych - 3;
break;
case 'w':
wysokosc = (wysokosc == WYS_MAX) ? WYS_MAX : wysokosc + 1;
break;
case 'W':
wysokosc = (wysokosc == 1) ? wysokosc : wysokosc - 1;
break;
case 'p':
promien = (promien == R_MAX) ? R_MAX : promien + 1;
break;
case 'P':
promien = (promien == 1) ? promien : promien - 1;
break;
case 'r':
UstawDomyslneWartosciParametrow();
break;
case '3':
if(roznica<2) roznica=roznica+0.25;
break;
case '#' :
if(roznica>1) roznica=roznica-0.25;
break;
case '7':
kat_y+=0.5;
break;
case '&':
kat_y-=0.5;
break;
case '1': glEnable(GL_LIGHT1);
break;
case '!': glDisable(GL_LIGHT1);
break;
case '2': glEnable(GL_LIGHT2);
break;
case '@': glDisable(GL_LIGHT2);
break;
case 27:
exit(0);
}
}
Odpowiednio klawisz 1/! Oraz 2/@ odpowiadają za włączanie i wyłączanie źródeł swiatła.
Klawisze 3/# to zmiana prędkości reflektora.
Klawisze 4/$ to zmiana promienia orbity reflektora.
Klawisze +/- to zmiana odległości obserwatora.
Klawisze v/b to obrót obserwatora względem osi Y.
Klawisze 8/* oraz 9/( odpowiadają za zmianę podziałów pionowych/poziomych walca.
Klawisze w/W oraz p/P to zmiana wysokości oraz promienia walca.
Wnioski
Celem ćwiczenia było zapoznanie się z modelowaniem oświetlenia za pomocą biblioteki OpenGL.
Dla początkujących programistów mierzących się z OpenGL modelowanie oświetlenia może być dość kłopotliwe. W OpenGL przypisując obiektowi parametry materiału , tak naprawdę przypisujemy mu kolor, który owy obiekt ma odbijać. Podczas przypisywania obiektowi koloru za pomocą funkcji glColor ,tak naprawdę jedynie ustawiamy charakterystykę opisującą kolor obiektu, który nie będzie w stanie wejść w żadną reakcję ze światłem. Jest to różnica między przypisywaniem obiektowi koloru , a parametrów materiału.
W rzeczywistości kiedy obiekt jest naświetlony białym światłem, niektóre długości fali światła są pochłaniane , a reszta jest odbijana. To właśnie odbijana cześć definiuje kolor obiektu.
Dla przykładu , czerwona odbija tylko czerwone `cząstki' światła a resztę pochłania.
Pod białym światłem wszystkie przedmioty maja swój naturalny kolor ,gdyż białe światło posiada wszystkie kolory, więc obiekt ma zawsze kolor , który może odbić. Gdy tą samą czerwoną kulę naświetlimy wyłącznie niebieskim światłem , owa kula będzie dla nas koloru czarnego , gdyż w wiązce światła nie będzie koloru czerwonego , który może odbić.
Na początku podczas tworzenia programu , materiały które były definiowane wyglądały zupełnie inaczej niż powinny. Widać to na pierwszym obrazie, przykładowo , żółty kolor tak naprawdę wygląda jak zgniło zielony. Spowodowane jest to brakiem oświetlenia. Dopiero po zaimplementowaniu reflektora białego światła , byłem w stanie sprawdzić czy materiały zostały dobrze zdefiniowane. Podobna sytuacja ma miejsce podczas naświetlenia obiektu dwoma źródłami światła( w tym wypadku biały i zielony). Obiekt znacząco się różni od obiektu oświetlonego wyłącznie reflektorem. Nałożenie się na oświetlanej powierzchni efektów świecenia kilku źródeł światła powoduje, że ta powierzchnia oświetlana jest światłem o parametrach wypadkowych, powstałych z nakładających się źródeł.
Kolejną istotną kwestią są podziały pionowe i poziome. Im jest ich więcej, tym efekt oświetlenia jest bardziej realistyczny. Jednak przy bardzo dużej liczbie podziałów szybkość działania programu drastycznie spada
Podsumowując modelowanie oświetlenia za pomocą biblioteki OpenGL wymaga sporo pracy i czasu, jednakże uzyskiwane efekty są tego warte.
.