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ł : Łukasz Kołodziejski
Grupa : I7X3S1
Data wykonania : 07.01.2009
Zestaw zadania : 18
TEMAT : MODELOWANIE OŚWIETLENIA
1. Treść zadania
Wykorzystując biblioteki OpenGL i GLUT napisać program przedstawiający perspektywiczny obraz obiektu o następujących parametrach:
Typ obiektu: walec o zmiennej parzystej liczbie podziałów pionowych i poziomych,
Właściwości materiału 1: brązowy błyszczący (widziany w białym świetle),
Właściwości materiału 2: czerwony matowy (widziany w białym świetle),
Sposób przyporządkowywania materiałów do obiektu zgodnie ze wzorem: szachownica z uwzględnieniem podziałów pionowych i poziomych.
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: 250,
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: fioletowy,
natężenie: 0.8,
położenie: stałe w punkcie P(10, 10, 10) układu współrzędnych sceny,
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.
2. Sposób wykonania zadania
Zadanie rozpocząłem od zdefiniowania materiałów oraz świateł.
//materialy
const GLfloat ambient_m1[4] = { 0.5, 0.3 , 0, 1 }; // braz blyszczacy
const GLfloat diffuse_m1[4] = { 0, 0, 0, 1 };
const GLfloat specular_m1[4] = { 0.5, 0.3, 0, 1 };
const GLfloat ambient_m2[4] = { 1, 0, 0, 1 }; // czerw matowy
const GLfloat diffuse_m2[4] = { 1, 0, 0, 1 };
const GLfloat specular_m2[4] = { 0, 0, 0, 1 };
//swiatla
const GLfloat ambient_s1[4] = { 1, 1, 1, 1 }; //spot
const GLfloat diffuse_s1[4] = { 1, 1, 1, 1 };
GLfloat specular_s1[4] = { 0, 0, 0, 1 };
GLfloat position_s1[] = { 5, 0, 0, 1 };
GLfloat direction_s1[] = { -90, 0, 0 };
const GLfloat ambient_s2[4] = {0.5, 0, 0.5, 1 }; //kierunkowe
const GLfloat diffuse_s2[4] = {0.8, 0.8, 0.8, 1 };
GLfloat specular_s2[4] = { 0, 0, 0, 1 };
GLfloat position_s2[] = { 10, 10, 10, 0 };
Składowe materiału pierwszego mają w nazwie końcówkę _m1 , materiału drugiego _m2 oraz świateł kolejno _s1 i s_2. Zapis ten jest dla mnie bardziej czytelny niż zapis w tablicy dwuwymiarowej, więc się nim posłużyłem. 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). Również składowe świateł wymagają wytłumaczenia. Otóż position służy do określenia pozycji światła w przestrzeni, a direction to kierunek w jakim świeci światło. Jak w załączonym kodzie widać, wszelkie parametry są ustawione zgodnie z poleceniem zadania.
Następnie napisałem funkcje, które ustawiają materiały i światła według naszych wartości :
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,10);
}
void Material2() {
glMaterialfv(GL_FRONT, GL_AMBIENT, ambient_m2);
glMaterialfv(GL_FRONT, GL_DIFFUSE, diffuse_m2);
glMaterialfv(GL_FRONT, GL_SPECULAR, specular_m2);
}
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_L1, 0, 0); // R_L1 - odległość od środka
glPushMatrix();
glutWireSphere(0.25, 20, 20); //światło jako kula
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, 25); //kąt odcięcia = 25
glLightfv(GL_LIGHT1, GL_SPOT_DIRECTION, direction_s1);
glPopMatrix();
}
void Swiatlo2() {
glLightfv(GL_LIGHT2, GL_DIFFUSE, diffuse_s2);
glLightfv(GL_LIGHT2, GL_SPECULAR, specular_s2);
glLightfv(GL_LIGHT2, GL_POSITION, position_s2);
}
GL_SHININESS określa w jakim stopniu ką, pod jakim obserwator widzi powierzchnię , musi być zgodny z kątem lustrzanego odbicia od niej promieni świetlnych , aby widoczny był efekt odblysku. Duże warości zawężają obszar występowania odbłysku, natomiast im mniejsza wartość , tym realistyczniejszy efekt można otrzymać. Wartość 0 sprawia, że zjawisko zachodzi zawsze, niezależnie od kąta obserwacji i kąta padania promieni światła. GL_SPOT_CUTOFF określa kąt odcięcia światła pozycyjnego. Światło rozprzestrzenia się w stożku o kącie rozwartym równym 2*GL_SPOT_CUTOF. Domyślną wartością, jest 180, co oznacza, że światło równomiernie rozhodzi się we wszystkch kierunkach.
Kolejnym etapem było stworzenie walca ze wzrem szachownicy. Funkcja realizująca to zadanie:
void RysujWalec(double h, double r, int nv, int nh)
{
double dH, dAlfa;
int i, j,parzystosc=0;
// 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++)
{
if (i%2 == 1) {
parzystosc = 1;
} else
parzystosc = 0;
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 (parzystosc%2 == 0) {
Material1();
parzystosc = 1;
} else {
Material2();
parzystosc = 0;
}
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%2 == 1) {
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%2 == 1) {
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. 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.
Program zawiera również możliwość zmiany parametrów podanych w zadaniu. Odpowiada za to funkja :
void ObslugaKlawiatury(unsigned char klawisz, int x, int y)
{
switch(klawisz)
{
//obserwator
case '+': OBSERWATOR_ODLEGLOSC++;
break;
case '-': OBSERWATOR_ODLEGLOSC--;
break;
case 'S': OBSERWATOR_WYSOKOSC++;
break;
case 's': OBSERWATOR_WYSOKOSC--;
break;
case 'a': OBSERWATOR_OBROT_Y++;
break;
case 'd': OBSERWATOR_OBROT_Y--;
break;
//podzial walca (lPionowych - to liczba podziałów w pionie itd.)
case '2':
lPionowych = (lPionowych == LPION_MAX)? LPION_MAX : lPionowych + 2;
break;
case '@':
lPionowych = (lPionowych == LPION_MIN)? LPION_MIN : lPionowych - 2;
break;
case '1':
lPoziomych = (lPoziomych == LPOZ_MAX)? LPOZ_MAX : lPoziomych + 2;
break;
case '!':
lPoziomych = (lPoziomych == LPOZ_MIN)? LPOZ_MIN : lPoziomych - 2;
break;
//wysokosc walca
case 'w':
wysokosc = (wysokosc == WYS_MAX) ? WYS_MAX : wysokosc + 1;
break;
case 'W':
wysokosc = (wysokosc == 1) ? wysokosc : wysokosc - 1;
break;
//promien walca
case 'p':
promien = (promien == R_MAX) ? R_MAX : promien + 1;
break;
case 'P':
promien = (promien == 1) ? promien : promien - 1;
break;
//reset
case 'r':
UstawDomyslneWartosciParametrow();
break;
case 'm':
menu = (menu == ID_MENU_SWIATLA) ? ID_MENU_MATERIALU : ID_MENU_SWIATLA;
break;
//predkosc kątowa źródła nr 1
case '3':
if(roznica<2) roznica=roznica+0.5;
break;
case '#' :
if(roznica>0.5) roznica=roznica-0.5;
break;
//kat nachyenia orbity do osi OX
case '7':
kat_y+=0.5;
break;
case '&':
kat_y-=0.5;
break;
//wlaczenie / wylaczenie swiatel
case '4': glEnable(GL_LIGHT1);
break;
case '$': glDisable(GL_LIGHT1);
break;
case '5': glEnable(GL_LIGHT2);
break;
case '%': glDisable(GL_LIGHT2);
break;
//promien orbity źródła nr 1
case '8': R_L1++;
break;
case '*': R_L1--;
break;
if (menu == ID_MENU_SWIATLA)
UstawParametryOswietlenia(sIndeks, klawisz);
else
UstawParametryMaterialu(mIndeks, klawisz);
break;
// Wcisniecie klawisza ESC powoduje wyjscie z programu
case 27:
exit(0);
}
}
Jak widać ,dzięki odpowiednim klawiszom możemy zmieniać interesujące nas wartości. W kodzie starałem się umieścić rozsądne komentarze, aby rozpoznać który klawisz odpowiada za jaką funkcję.
3. UZYSKANE WYNIKI
Jak widać na załączonych obrazkach w programie istnieje możliwość zmiany wszelkich parametrów, takich jak wysokość, promień -zaróno walca jak i źródła światła, oraz liczby podziałów pionowych i poziomych walca.
4. WNIOSKI
Zadanie laboratoryjne zostało zrealizowane. Cel zapoznania się z modelowaniem oświetlenia za pomocą biblioteki OpenGL został osiągnięty. Dobierając odpowiednie wartości dla materiałów jak i źródeł światła, zaoberwowałem wiele efektów. Otóż same zmiany były łatwe do wykonania i wystarczyło tylko dobierać wartości, aby uzyskać oczekiwane wyniki. Zwróciłem też uwagę, że przy dużej liczbie podziałów walca oraz przy włączonym świetle, program potrafi zwolnić swoje działanie. Co prawda uzyskane efekty są o wiele ładniejsze, aczkolwiek problem z klatkowaniem obrazu zmniejsza ochotę oglądania takich wyników. Jednakże praca laboratoryjna była ciekawa , a efekty ukazywały ogromne możliwości OpenGL'a.