Złamański L4[1]


WOJSKOWA AKADEMIA TECHNICZNA
im. Jarosława Dąbrowskiego
LABORATORIUM GRAFIKI KOMPUTEROWEJ
SPRAWOZDANIE Z ĆWICZENIA LABORATORYJNEGO NR 4.
SPRAWOZDANIE WYKONAA: TOMASZ ZAAMACSKI
DATA WYKONANIA ĆWICZENIA: 09.01.2009.
GRUPA: I7X5S1
1. Treść zadania:
Wykorzystując biblioteki OpenGL i GLUT napisać program przedstawiający perspektywiczny
obraz obiektu o następujących parametrach:
1. Typ obiektu: walec o zmiennej parzystej liczbie podziałów pionowych i poziomych,
2. Właściwości materiału 1: brązowy błyszczący (widziany w białym świetle),
3. Właściwości materiału 2: fioletowy emitujący (widziany w białym świetle),
4. Sposób przyporządkowywania materiałów do obiektu zgodnie ze wzorem: pasy poziome z
uwzględnieniem podziałów poziomych.
Obiekt należy oświetlić dwoma zródłami światła o następujących parametrach:
yródło nr 1:
" typ: reflektor (ang. spot),
" kolor: zółty,
" 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.
yródło nr 2:
" typ: kierunkowe,
" kolor: niebieski,
" natężenie: 0.8,
" 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 zródeł światła;
b) interaktywną zmianę liczby podziałów pionowych i poziomych bryły;
c) interaktywną zmianę wielkości bryły;
- 2 -
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 [00, 3600] z krokiem 10.
Oświetlony obiekt powinien zawsze znajdować się w centralnej części okna.
- 3 -
2. Sposób wykonania zadania:
Realizację treści zadania rozpocząłem od zdefiniowania parametrów materiałów:
// Materialy
GLfloat material1[3][4] = { // brazowy blyszczacy
{ 0.8f, 0.3f, 0.3f, 1.0f }, // ambient
{ 0.0f, 0.0f, 0.0f, 1.0f }, // diffuse
{ 0.8f, 0.3f, 0.3f, 1.0f } // specular
};
GLfloat material2[4][4] = { // fioletowy emitujacy
{ 1.0f, 0.0f, 1.0f, 1.0f }, // ambient
{ 1.0f, 0.0f, 1.0f, 1.0f }, // diffuse
{ 1.0f, 0.0f, 1.0f, 1.0f }, // specular
{ 0.1f, 0.0f, 0.1f, 1.0f } // emission
};
Jak widać na powyższym listingu całość sprowadziła się do utworzenia dwóch tablic
dwuwymiarowych, które zawierają odpowiednio ustawione składowe RGBA poszczególnych
parametrów.
Składowe parametru ambient określają stopień odbicia światła otaczającego,
parametru diffuse  stopień rozproszenia światła rozproszonego, parametru specular 
stopień odbicia światła odbitego, zaś składowe RGBA parametru emission określają światło
emitowane przez obiekt. Ten ostatni parametr domyślnie jest ustawiony tak, że materiał nie
jest emitujący. Dlatego pierwsza tablica nie zawiera ustawień tego parametru.
Dobranie powyższych parametrów nie jest zadaniem łatwym i przysporzyło mi nieco
kłopotów, jednakże udało się osiągnąć zadowalający efekt. Przy znajdowaniu ustawień dla
pierwszego materiału kierowałem się zasadą, która mówi, że powierzchnie błyszczące
powinny mieć małe składowe parametru diffuse, zaś spore parametru specular. Materiał
drugi wymagał jedynie dobrania odpowiednich wartości dla parametru emission.
Następnym krokiem było napisanie funkcji, które będą korzystały ze zdefiniowanych
materiałów w celu ustawienia ich w odpowiednim momencie modelowania obiektu. Funkcje
te przedstawiają się następująco:
// Funkcja ustawiajaca material 1
void setMaterial1() {
glMaterialfv(GL_FRONT, GL_AMBIENT, material1[0]);
glMaterialfv(GL_FRONT, GL_DIFFUSE, material1[1]);
glMaterialfv(GL_FRONT, GL_SPECULAR, material1[2]);
glMaterialf(GL_FRONT, GL_SHININESS, 128.0f);
}
// Funkcja ustawiajaca material 2
void setMaterial2() {
glMaterialfv(GL_FRONT, GL_AMBIENT, material2[0]);
glMaterialfv(GL_FRONT, GL_DIFFUSE, material2[1]);
glMaterialfv(GL_FRONT, GL_SPECULAR, material2[2]);
glMaterialf(GL_FRONT, GL_SHININESS, 10.0f);
glMaterialfv(GL_FRONT, GL_EMISSION, material2[3]);
}
Integralnym elementem modelu oświetlenia przyjętego w bibliotece OpenGL jest opis
sposobu zachowania się powierzchni obiektów na poszczególne rodzaje światła, czyli opis
właściwości materiałów. Modyfikowanie właściwości materiałów umożliwiają funkcje z
grupy glMaterial, których wykorzystanie widoczne jest na powyższym listingu.
- 4 -
Kolejną czynnością było zdefiniowanie parametrów zródeł światła. Realizuje to
poniższy fragment kodu:
// Zrodla swiatla
bool light1Src = false;
bool light2Src = false;
GLfloat light1[4][4] = { // zolty reflektor (natezenie 1)
{ 1.0f, 1.0f, 0.0f, 1.0f}, // diffuse
{ 1.0f, 1.0f, 0.0f, 1.0f}, // specular
{ 0.0f, 0.0f, 1.0f, 1.0f}, // position
{ 0.0f, 0.0f, -1.0f} // spot direction
};
GLfloat light2[4][4] = { // niebieskie kierunkowe (natezenie 0.8)
{ 0.0f, 0.0f, 1.0f, 0.8f}, // diffuse
{ 0.0f, 0.0f, 1.0f, 0.8f}, // specular
{ -10.0f, -5.0f, 10.0f, 1.0f}, // position
{ 10.0f, 5.0f, -10.0f} // spot direction
};
Zmienne boolowskie określają, czy dane zródło światła jest włączone  domyślnie oba
zródła są wyłączone.
Parametr position określa położenie zródła światła, jak się okaże składowe tego
parametru podane są w układzie współrzędnych sceny. Parametr spot direction to
znormalizowany, trójwspółrzędny wektor określający kierunek reflektora.
Tym razem odpowiednie dobranie powyższych parametrów nie było już takie trudne.
Zatem odbyło się to szybko i przeszedłem do realizacji funkcji ustawiających wyżej
zdefiniowane zródła światła:
// Funkcja ustawiajaca zrodlo swiatla 1
void setLight1() {
glPushMatrix();
// Ustawienie polozenia
glRotatef(l1RotX, 1.0f, 0.0f, 0.0f);
glTranslatef(0.0f, 0.0f, l1Radius);
// Ustawienie zrodla w postaci kuli
glPushMatrix();
glDisable(GL_LIGHTING);
glColor3f(1.0f, 1.0f, 0.0f);
glutSolidSphere(1.0, 30, 30);
glEnable(GL_LIGHTING);
glPopMatrix();
// Ustawienie parametrow swiatla
glLightfv(GL_LIGHT0, GL_DIFFUSE, light1[0]);
glLightfv(GL_LIGHT0, GL_SPECULAR, light1[1]);
glLightfv(GL_LIGHT0, GL_POSITION, light1[2]);
glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, light1[3]);
glLightf (GL_LIGHT0, GL_SPOT_EXPONENT, 1.0f);
glLightf (GL_LIGHT0, GL_SPOT_CUTOFF, 30.0f);
glPopMatrix();
}
// Funkcja ustawiajaca zrodlo swiatla 2
void setLight2() {
glLightfv(GL_LIGHT1, GL_DIFFUSE, light2[0]);
glLightfv(GL_LIGHT1, GL_SPECULAR, light2[1]);
glLightfv(GL_LIGHT1, GL_POSITION, light2[2]);
glLightfv(GL_LIGHT1, GL_SPOT_DIRECTION, light2[3]);
}
- 5 -
Parametry każdego ze zródła światła można modyfikować niezależnie od pozostałych
zródeł światła. Służą do tego funkcje z grupy glLight, których zastosowanie widać na
powyższym listingu.
Słowo komentarza należy się pierwszej funkcji. Mianowicie jak widać położenie zródła
światła nr 1 zależy od pewnych zmiennych, które użytkownik może interaktywnie zmieniać.
Z powyższego listingu wynika także, że reprezentacją tegoż zródła jest kula o kolorze
żółtym. Parametr spot exponent to wykładnik tłumienia kątowego reflektora, zaś parametr
spot cutoff to po prostu kąt odcięcia reflektora.
Nadszedł czas na stworzenie walca o zmiennej parzystej liczbie podziałów poziomych i
pionowych. Zadanie to realizuje poniższa funkcja:
// Funkcja rysujaca walec
// r - promien podstawy
// h - wysokosc
void drawCylinder(float r, float h) {
// Rysowanie ukladu wspolrzednych
drawCoordSystem();
float tr = r; // tymczasowy promien
float phi = -M_PI; // przechodzenie w poziomie
float x1, y1, z1; // wspolrzedne punktu 1
float x2, y2, z2; // wspolrzedne punktu 2
float x3, y3, z3; // wspolrzedne punktu 3
float x4, y4, z4; // wspolrzedne punktu 4
bool mat = true; // domyslnie material 1
glPushMatrix();
// Rysowanie sciany bocznej
for (int i = 0; i < horiDivs; ++i) {
if (mat == true)
setMaterial1();
else setMaterial2();
mat = !mat;
for (int j = 0; j < vertDivs; ++j, phi += 2 * M_PI / vertDivs) {
glBegin(GL_QUADS);
// Definiowanie punktu 1
x1 = r * sin(phi);
y1 = i * h / horiDivs - h / 2.0f;
z1 = r * cos(phi);
glVertex3f(x1, y1, z1);
// Definiowanie punktu 2
x2 = r * sin(phi + 2 * M_PI / vertDivs);
y2 = i * h / horiDivs - h / 2.0f;
z2 = r * cos(phi + 2 * M_PI / vertDivs);
glVertex3f(x2, y2, z2);
// Definiowanie punktu 3
x3 = r * sin(phi + 2 * M_PI / vertDivs);
y3 = (i + 1) * h / horiDivs - h / 2.0f;
z3 = r * cos(phi + 2 * M_PI / vertDivs);
glVertex3f(x3, y3, z3);
// Definiowanie punktu 4
x4 = r * sin(phi);
y4 = (i + 1) * h / horiDivs - h / 2.0f;
z4 = r * cos(phi);
glVertex3f(x4, y4, z4);
// Definiowanie wektora 1
v1[0] = x2 - x1;
v1[1] = y2 - y1;
v1[2] = z2 - z1;
// Definiowanie wektora 2
- 6 -
v2[0] = x4 - x1;
v2[1] = y4 - y1;
v2[2] = z4 - z1;
// Obliczenie wektora normalnego
normal(v1, v2, v3);
glNormal3f(v3[0], v3[1], v3[2]);
glEnd();
}
}
// Rysowanie dolnej podstawy
tr = r;
phi = 0.0f;
mat = false;
for (int i = 0; i < horiDivs; ++i, tr -= r / horiDivs) {
if (mat == true)
setMaterial1();
else setMaterial2();
mat = !mat;
for (int j = 0; j < vertDivs; ++j, phi += 2 * M_PI / vertDivs) {
glBegin(GL_QUADS);
// Definiowanie punktu 1
x1 = tr * sin(phi);
y1 = - h / 2.0f;
z1 = tr * cos(phi);
glVertex3f(x1, y1, z1);
// Definiowanie punktu 2
x2 = tr * sin(phi + 2 * M_PI / vertDivs);
y2 = - h / 2.0f;
z2 = tr * cos(phi + 2 * M_PI / vertDivs);
glVertex3f(x2, y2, z2);
// Definiowanie punktu 3
x3 = (tr - r / horiDivs) * sin(phi + 2 * M_PI / vertDivs);
y3 = - h / 2.0f;
z3 = (tr - r / horiDivs) * cos(phi + 2 * M_PI / vertDivs);
glVertex3f(x3, y3, z3);
// Definiowanie punktu 4
x4 = (tr - r / horiDivs) * sin(phi);
y4 = - h / 2.0f;
z4 = (tr - r / horiDivs) * cos(phi);
glVertex3f(x4, y4, z4);
// Ustawienie wektora normalnego
glNormal3f(0.0f, 1.0f, 0.0f);
glEnd();
}
}
// Rysowanie gornej podstawy
tr = r;
phi = 0.0f;
mat = true;
for (int i = 0; i < horiDivs; ++i, tr -= r / horiDivs) {
if (mat == true)
setMaterial1();
else setMaterial2();
mat = !mat;
for (int j = 0; j < vertDivs; ++j, phi += 2 * M_PI / vertDivs) {
glBegin(GL_QUADS);
// Definiowanie punktu 1
x1 = tr * sin(phi);
y1 = h / 2.0f;
z1 = tr * cos(phi);
glVertex3f(x1, y1, z1);
// Definiowanie punktu 2
x2 = tr * sin(phi + 2 * M_PI / vertDivs);
- 7 -
y2 = h / 2.0f;
z2 = tr * cos(phi + 2 * M_PI / vertDivs);
glVertex3f(x2, y2, z2);
// Definiowanie punktu 3
x3 = (tr - r / horiDivs) * sin(phi + 2 * M_PI / vertDivs);
y3 = h / 2.0f;
z3 = (tr - r / horiDivs) * cos(phi + 2 * M_PI / vertDivs);
glVertex3f(x3, y3, z3);
// Definiowanie punktu 4
x4 = (tr - r / horiDivs) * sin(phi);
y4 = h / 2.0f;
z4 = (tr - r / horiDivs) * cos(phi);
glVertex3f(x4, y4, z4);
// Ustawienie wektora normalnego
if (i == 0 && j == 0)
glNormal3f(0.0f, 1.0f, 0.0f);
glEnd();
}
}
glPopMatrix();
// Ustawienie zrodel swiatla
setLight1();
setLight2();
}
Jak widać z powyższego listingu, modelowanie walca odbywa się w trzech etapach.
Najpierw tworzona jest ściana boczna. Na samym początku zewnętrznej pętli przechodzącej
przez wszystkie podziały poziome, ustawiany jest materiał. To jaki zostanie wybrany, zależy
od aktualnego ustawienia zmiennej mat. Wewnątrz pętli wewnętrznej, przechodzącej przez
wszystkie podziały pionowe, definiowane są odpowiednie punkty (wierzchołki
prostokątów). Jest ich 4, gdyż zdecydowałem się na tryb GL_QUADS.
Jednak do prawidłowego generowania efektów oświetlenia, a w szczególności
określenia orientacji wierzchołków obiektów względem zródeł światła, biblioteka OpenGL
wymaga definiowania wektorów normalnych (prostopadłych). Tym zajmuje się zestaw
instrukcji pod koniec wewnętrznej pętli (wykorzystany jest iloczyn wektorowy).
Rysowanie podstaw odbywa się w bardzo podobny sposób. Na koniec funkcji rysującej
walec znajduje się wywołanie funkcji ustawiającej światło. Jest ważne, aby wszystkie
parametry ustawić tuż po narysowaniu obiektu.
Ostateczny efektbez uwzględnienia oświetlenia prezentuje się następująco:
- 8 -
Walec po prawej ma mniejszą liczbę podziałów poziomych, a także pionowych. Jak się za
chwilę okaże ma to ogromny wpływ na  jakość generowanego oświetlenia. Oto co się stanie,
gdy włączone zostaje zródło światła nr 1:
Zdecydowanie widać różnicę. Poniżej prezentuję co się stanie gdy dodatkowo włączone
zostaje zródło światła kierunkowego (nr 2):
Jak widać nałożenie się na siebie świateł
pochodzących od obu zródeł powoduje ciekawe
efekty w postaci nałożenia się kolorów i tym
samym oświetlenia powierzchni na zupełnie inny
kolor, wynikający z tegoż nałożenia.
A po prawej efekt samego zródła światła
nr 2. Jak widać zgodnie z treścią zadania świeci
ono na niebiesko.
- 9 -
Niestety na statycznym rysunku nie jestem w stanie pokazać ruchu pierwszego zródła
światła, jednakże okrąża ono oś OX z ustaloną prędkością.
Zgodnie z treścią zadania program miał umożliwiać zmianę różnych parametrów. Mój
program uwzględnia te wymagania i zawiera poniższą funkcję obsługi klawiatury:
// Funkcja obslugujaca klawiature
// key - kod ASCII klawisza
// x, y - wspolrzedne kursora myszki w chwili nacisniecia klawisza
void keyboard(unsigned char key, int x, int y) {
switch (key) {
case '1':
if (light1Src == false)
glEnable(GL_LIGHT0);
else glDisable(GL_LIGHT0);
light1Src = !light1Src;
break;
case '2':
if (light2Src == false)
glEnable(GL_LIGHT1);
else glDisable(GL_LIGHT1);
light2Src = !light2Src;
break;
Pierwsze dwa przypadki dotyczą włączania/wyłączania zródeł światła. Naciśnięcie
klawisza 1, bądz 2 spowoduje odpowiednią czynność dla odpowiadającego klawiszowi
zródle światła. Ta czynność to włączenie bądz wyłączenie w zależności od aktualnego stanu
zmiennych boolowskich.
case 'q':
l1Radius = (l1Radius < 16.0f ? l1Radius + 0.5f : l1Radius);
break;
case 'e':
l1Radius = (l1Radius > 12.0f ? l1Radius - 0.5f : l1Radius);
break;
Dwa kolejne przypadki dotyczą zmiany promienia orbity zródła światła nr 1.
case 'a':
if (l1Speed > 1.0f)
l1Speed -= 1.0f;
break;
case 'd':
if (l1Speed < 3.0f)
l1Speed += 1.0f;
break;
Za pomocą klawiszy a i d jesteśmy w stanie zmieniać prędkość kątową zródła światła 1.
case 'r':
if (vertDivs < 100)
vertDivs += 2;
break;
case 'f':
if (vertDivs > 30)
vertDivs -= 2;
break;
case 't':
if (horiDivs < 60)
horiDivs += 2;
break;
case 'g':
if (horiDivs > 30)
horiDivs -= 2;
break;
Powyższe klawisze służą do zmiany liczby podziałów pionowych (r, f) i poziomych (t, g).
case 'z':
if (objectSize > 0.85f)
objectSize -= 0.05f;
break;
- 10 -
case 'x':
if (objectSize < 1.5f)
objectSize += 0.05f;
break;
Za pomocą tych klawiszy można zmieniać wielkość obiektu.
case '+':
if (radius > 25.0)
radius -= 3.0;
break;
case '-':
if (radius < 75.0)
radius += 3.0;
break;
Przybliżanie i oddalanie obserwatora od obiektu.
case EXIT:
exit(0);
default:
break;
}
l1RotX = (l1RotX < 0.0f ? l1RotX + 360.0f :
l1RotX > 360.0f ? l1RotX - 360.0f : l1RotX);
Ta instrukcja powyżej jest potrzebna, ponieważ l1Rotx zawiera się w przedziale [00, 3600].
// Odrysowanie okna
reshape(glutGet(GLUT_WINDOW_WIDTH), glutGet(GLUT_WINDOW_HEIGHT));
}
Oprócz powyższej funkcji zaimplementowałem także funkcję obsługi klawiszy specjalnych:
// Funkcja obslugujaca klawisze funkcyjne i klawisze kursora
// key - kod przycisku
// x, y - wspolrzedne kursora myszki w chwili nacisniecia klawisza
void specialKeys(int key, int x, int y) {
switch (key) {
case GLUT_KEY_UP:
phi -= 3.0f;
break;
case GLUT_KEY_DOWN:
phi += 3.0f;
break;
case GLUT_KEY_LEFT:
theta -= 3.0f;
break;
case GLUT_KEY_RIGHT:
theta += 3.0f;
break;
default:
break;
}
phi = (phi < 0.0 ? phi + 360.0 : phi > 360.0 ? phi - 360 : phi);
theta = (theta < 0.0 ? theta + 360.0 : theta > 360.0 ? theta - 360 : theta);
// Odrysowanie okna
reshape(glutGet(GLUT_WINDOW_WIDTH), glutGet(GLUT_WINDOW_HEIGHT));
}
Jak widać obsługuje ona jedynie położenie obserwatora. Zmienne phi oraz theta
wykorzystywane są w obliczeniach położenia obserwatora. Należy bowiem zauważyć, że
obserwator zrealizowany został w układzie współrzędnych sferycznych (porusza się po
sferze, w której środku jest obserwowany obiekt), zatem pojawienie się tych dwóch
zmiennych jest naturalne.
- 11 -
3. Uzyskane wyniki:
Podczas wykonywania ćwiczenia zauważyłem, że tworzenie materiałów, z których
obiekt  korzysta oferuje wielką swobodę. Polega na manipulowaniu odpowiednimi
parametrami. Samo dobranie wartości tychże parametrów nie jest rzeczą łatwą, jednakże
dostarczony mechanizm oferuje w ten sposób potężne możliwości.
Co więcej, nałożenie się na oświetlanej powierzchni efektów świecenia kilku zródeł
światła powoduje, że ta powierzchnia oświetlana jest światłem o parametrach wypadkowych,
powstałych z nakładających się zródeł. Według mnie jest to jednak zaletą, gdyż umożliwia
zmiany kolorów za pomocą manipuacji parametrami oświetlenia, tak jak to codziennie można
zaobserwować w otaczającym nas świecie. Dzięki temu modelowanie scen jest dużo łatwiejsze,
gdyż w celu zmiany pory dnia należy jedynie ustawić światło odpowiednie dla niej, a nie
zmieniać kolory czy tekstury wypełniające modele.
Istotną kwestią są podziały pionowe i poziome. Im jest ich więcej, tym efekt oświetlenia
jest bardziej realistyczny. Należy jednak uważać aby nie przesadzić, gdyż przy bardzo dużej
liczbie podziałów działanie programu jest wyraznie spowolnione.
- 12 -


Wyszukiwarka

Podobne podstrony:
zlamania twarzoczaszki
aisde l4
K4 L4
instrukcja pierwszej pomocy postepowanie w przypadku zlaman
L4 regresja liniowa klucz
L4 new
Kurasz Arkadiusz ST L4 CW7

więcej podobnych podstron