WOJSKOWA AKADEMIA TECHNICZNA
INSTYTUT TELEINFORMATYKI I AUTOMATYKI
Laboratorium - Grafika Komputerowa
Sprawozdanie
Ćwiczenie laboratoryjne nr 3
Przekształcenia geometryczne
Student: Marcin Kaiser
Grupa: I5B1N0
Data wykonania: 24.01.2009 (z grupą I7X1N1)
Treść zadania
Sposób wykonania zadania, przyjęta metoda, algorytm
Do wykonania zadania użyłem prymitywów przestrzennych udostępnianych przez biblioteki GLU i GLUT.
Modelowanie lampki zrealizowałem w 4 etapach:
1) modelowanie podstawy
2) modelowanie wspornika
3) modelowanie ramienia
4) modelowanie lampy.
Na początku zdefiniowałem wskaźniki do kwadryk
GLUquadricObj *podstawa;
GLUquadricObj *podstawaDyskG;
GLUquadricObj *podstawaDyskD;
GLUquadricObj *czescKlosza;
GLUquadricObj *czescDyskG;
GLUquadricObj *czescDyskD;
GLUquadricObj *klosz;
GLUquadricObj *kloszDyskG;
oraz zainicjowałem je w funkcji Inicjujlampke
void Inicjujlampke(void)
{
// Zainicjowanie scian bocznych walca bedacego podstawa lampki
podstawa = gluNewQuadric();
gluQuadricDrawStyle(podstawa, GLU_LINE);
// Zainicjowanie gornej podstawy walca
podstawaDyskG = gluNewQuadric();
gluQuadricDrawStyle(podstawaDyskG, GLU_LINE);
// Zainicjowanie dolnej podstawy walca
podstawaDyskD = gluNewQuadric();
gluQuadricDrawStyle(podstawaDyskD, GLU_LINE);
// Zainicjowanie scian bocznych walca gornej czesci klosza
czescKlosza = gluNewQuadric();
gluQuadricDrawStyle(czescKlosza, GLU_LINE);
// Zainicjowanie gornej podstawy walca
czescDyskG = gluNewQuadric();
gluQuadricDrawStyle(czescDyskG, GLU_LINE);
// Zainicjowanie dolnej podstawy walca
czescDyskD = gluNewQuadric();
gluQuadricDrawStyle(czescDyskD, GLU_LINE);
// Zainicjowanie scian bocznych stozka bedacego kloszem
klosz = gluNewQuadric();
gluQuadricDrawStyle(klosz, GLU_LINE);
// Zainicjowanie gornej podstawy walca
kloszDyskG = gluNewQuadric();
gluQuadricDrawStyle(kloszDyskG, GLU_LINE);
}
Modelowanie podstawy
//podstawa lampki
glPushMatrix();
glColor3f(1.0,1.0,1.0);
glRotatef(-90.0,1,0,0);
gluCylinder(podstawa,2.5,2.5,0.5,20,4);
gluDisk(podstawaDyskD,0.0,2.5,20,4);
glTranslatef(0.0,0.0,0.5);
gluDisk(podstawaDyskG,0.0,2.5,20,4);
glPopMatrix();
Przy pomocy funkcji glColor3f() ustawiłem kolor na biały. Kolor ten pozostaje obowiązujący również w później modelowanych elementach. Funkcja glRotatef() zmienia płaszczyznę modelowanej podstawy z płaszczyzny XY na płaszczyznę XZ poprzez obrót o -90o wokół osi X. Funkcja gluCylinder() wyświetla walec o promieniu dolnej i górnej podstawy równym 2,5 i o wysokości 0,5. Walec ten jest bokiem podstawy. Aby podstawa była kompletna rysuję dolne zamknięcie, a następnie - po przesunięciu o 0,5 - górne zamknięcie za pomocą funkcji gluDisk(). Wszystkie powyższe operacje wykonuję na osobnej macierzy.
Zanim przechodzę do modelowania dalszych elementów, wykorzystuję funkcję glRotatef do umożliwienia obrotu tych dalszych elementów wokół osi Y
//rotacja lampki
glRotatef(obrotGlowicy, 0, 1, 0);
Parametr obrotGlowicy odpowiada za kąt obrotu, zmieniany później w zakresie
0 - 360o przy pomocy klawiszy 3 i #
Modelowanie wspornika
//wspornik
glPushMatrix();
glTranslatef(0.0,0.5+2.5,0.0);
glScalef(0.5,5.0,0.5);
glutWireCube(1);
glPopMatrix();
Do jego narysowania używam funkcji glutWireCube(). Po wywołaniu tej funkcji otrzymuję szkielet sześcianu, który skaluję przy pomocy funkcji glScalef() tak aby otrzymać szkielet prostopadłościanu o wymiarach 0,5 na 0,5 na 5. Ponadto, ponieważ środek sześcianu leży w środku układu współrzędnych, przesuwam go
w górę o wartość równą połowie wysokości ramienia plus grubość podstawy przy pomocy funkcji glTranslate(). Wszystkie powyższe operacje wykonuję na osobnej macierzy.
W celu modelowania dalszych elementów przesuwam środek układu współrzędnych z punktu (0,0,0) do punktu (-0.4 , 5 , 0) przy pomocy funkcji glTranslate() oraz umożliwiam obrót tych elementów wokół osi X przy pomocy funkcji glRotate().
glTranslatef(-0.4,0.5+4.5,0.0);
glRotatef(obrotRamienia1, 1, 0, 0);
Parametr obrotRamienia1 odpowiada za kąt obrotu i zmieniany jest później
w zakresie -30o do 60o przy pomocy klawiszy 2 i @.
Modelowanie ramienia
//ramie
glPushMatrix();
glTranslatef(0.0,0.0,-2.0);
glScalef(0.25,0.25,5.0);
glutWireCube(1);
glPopMatrix();
Do jego narysowania używam funkcji glutWireCube(). Po wywołaniu tej funkcji otrzymuję szkielet sześcianu (już w nowym środku układu współrzędnych), który skaluję przy pomocy funkcji glScalef(), tak aby otrzymać szkielet prostopadłościanu
o wymiarach 0,25 na 0,25 na 5. Ponadto przesuwam go wzdłuż osi Z o wartość -2 przy pomocy funkcji glTranslate(). Wszystkie powyższe operacje wykonuję na osobnej macierzy.
Modelowanie lampy (części klosza)
//czesc klosza
glPushMatrix();
glRotatef(-90.0,1,0,0);
glTranslatef(0.0,4.5,-0.5);
gluCylinder(czescKlosza,0.25,0.25,1.0,20,4);
gluDisk(czescDyskD,0.0,0.25,20,4);
glTranslatef(0.0,0.0,1.0);
gluDisk(czescDyskG,0.0,0.25,20,4);
glPopMatrix();
Funkcja gluCylinder() wyświetla walec o promieniu dolnej i górnej podstawy równym 0,25 i wysokości 1, będący bokiem części klosza. Aby część klosza była kompletna rysuję dolne zamknięcie, a następnie - po przesunięciu o 1 - górne zamknięcie za pomocą gluDisk(). Walec przesuwam wzdłuż osi Y o wartość 4,5 i wzdłuż osi Z
o wartość -0,5 przy pomocy funkcji glTranslate() oraz przemieszczam z obrotem
o 90o wokół osi X zgodnie z ruchem wskazówek zegara przy pomocy funkcji glRotatef(), tak że trafia on na koniec ramienia. Wszystkie powyższe operacje wykonuję na osobnej macierzy.
Modelowanie lampy (klosza)
//klosz
glPushMatrix();
glRotatef(-90.0,1,0,0);
glTranslatef(0.0,4.5,-0.5-1.0);
gluCylinder(klosz,1.0,0.5,1.0,20,4);
glTranslatef(0.0,0.0,1.0);
gluDisk(kloszDyskG,0.0,0.5,20,4);
glPopMatrix();
Funkcja gluCylinder() wyświetla rozszerzający się cylinder o promieniu dolnej podstawy równym 1 i górnej podstawy równym 0,5 i wysokości 1. Cylinder jest bokiem klosza. Aby klosz był kompletny rysuję górne zamknięcie za pomocą gluDisk(). Klosz przesuwam wzdłuż osi Y o wartość 4,5 i wzdłuż osi Z o wartość -1,5 przy pomocy funkcji glTranslate() oraz przemieszczam z obrotem o 90o wokół osi X zgodnie z ruchem wskazówek zegara przy pomocy funkcji glRotatef(), tak że trafia on pod część klosza. Wszystkie powyższe operacje wykonuję na osobnej macierzy.
Stworzenie obserwatora zgodnie z założeniami
// Funkcja generujaca pojedyncza klatke animacji
void WyswietlObraz(void)
{
// Wyczyszczenie bufora ramki i bufora glebokosci
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
// Powielenie macierzy na wierzcholku stosu
glPushMatrix();
// Wyznaczenie polozenia obserwatora (przeksztalcenie uladu wspolrzednych
// sceny do ukladu wspolrzednych obserwatora).
zmienna=RAD2DEG(atan(obs_wys/obs_odl));
glTranslatef(0,0, -obs_odl);
glRotatef(zmienna,1,0,0);
glRotatef(rotObsY,0,1,0);
// Generacja obrazu sceny w niewidocznym buforze ramki
RysujLampke(rotRamienia1,rotGlowicy);
// Usuniecie macierzy lezacej na wierzcholku stosu (powrot do stanu
// sprzed wywolania funkcji)
glPopMatrix();
// Przelaczenie buforow ramki
glutSwapBuffers();
}
Parametr obs_wys odpowiada za wysokość obserwatora, a obs_odl za odległość obserwatora od obiektu. Przy zmianie jednego z tych parametrów liczony jest arctg(obs_wys/obs_odl), dzięki czemu wyznaczam kąt pomiędzy płaszczyzną na której postawiona jest lampka oraz linią łączącą środek układu współrzędnych
z obserwatorem.
Uzyskany wynik
Bezpośrednio po uruchomieniu programu, przed użyciem klawiszy nawigacyjnych:
Przykładowy wynik po powiększeniu, opuszczeniu ramienia i zmianie pozycji obserwatora:
Wszystkie elementy rysowane są w oddzielnych macierzach. Aby lampce nadać odpowiednie zachowania, tzn obrót po zadanych osiach, wszystkie operacje obracania wykonywane są na jednej, wspólnej macierzy. Np.: rotacja wokół osi X obraca ramieniem oraz lampą (kloszem i częścią klosza). Gdybym dołączył funkcję obrotu np. do funkcji rysującej ramię (operującej na swojej macierzy), wtedy zmiana parametru obrotRamienia1 spowodowałaby obrót tylko tego ramienia, ale już nie klosza i części klosza.
Obsługa klawiszy
2 - podniesienie ramienia lampki
@ - opuszczenie ramienia lampki
3 - obrót głowicy w kierunku przeciwnym do kierunku wskazówek zegara
# - obrót głowicy w kierunku zgodnym z kierunkiem wskazówek zegara
= - zbliżenie obserwatora
+ - oddalenie obserwatora
strzałki - zmiana orientacji obserwatora względem osi X,Y,Z
Wnioski
W celu zrealizowania zadania potrzebna była wiedza w zakresie przekształceń geometrycznych w przestrzeni 3D. Mogłem stwierdzić, że kolejność składania przekształceń ma znaczenie. Zapoznałem się z podstawowymi funkcjami bibliotek GLU i GLUT służącymi do modyfikacji kształtu i położenia obiektów oraz
z prymitywami przestrzennymi udostępnionymi przez te biblioteki.