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

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

  1. Typ obiektu: walec o zmiennej podzielnej przez 3 liczbie podziałów pionowych i poziomych,

  2. Właściwości materiału 1: fioletowy błyszczący (widziany w białym świetle),

  3. Właściwości materiału 2: żółty matowy (widziany w białym świetle),

  4. Właściwości materiału 3: zielony emitujący (widziany w białym świetle),

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

Źródło nr 2:

Program powinien umożliwiać:

    1. interaktywne, niezależne włączanie i wyłączanie źródeł światła;

    2. interaktywną zmianę liczby podziałów pionowych i poziomych bryły;

    3. interaktywną zmianę wielkości bryły;

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

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

  1. Wyniki działania programu.

1 2

0x01 graphic
0x01 graphic

3 4

0x01 graphic
0x01 graphic

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.

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

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

.