WOJSKOWA AKADEMIA TECHNICZNA
Laboratorium z przedmiotu
Grafika Komputerowa
SPRAWOZDANIE
Laboratorium nr 3 – przekształcenia geometryczne
Piotr Rusinowski
I7X6S1
Prowadzący
dr inż. Marek Salamon
Opis zadania
Napisać program przedstawiający program zbudowany z prymitywów przestrzennych udostępnionych przez biblioteki GLU i GLUT. Użytkownik za pomocą klawiatury powinien mieć możliwość wprowadzenia zmian następujących parametrów:
Kąt obrotu wieży w zakresie [0;360] stopni;
Kąt podniesienia armaty w zakresie [0;60] stopni;
W programie uwzględnić możliwość zmiany położenia obserwatora poprzez podanie następujących parametrów:
Odległość obserwatora od obiektu;
Orientacji obserwatora w zakresie [0;360] stopni względem osi OX, OY, OZ;
Sposób rozwiązania
Najpierw zacząłem od deklaracji wskaźników na dwie kwadryki, których potem będę używał w programie:
GLUquadricObj *kwadryka;
GLUquadricObj *kwadrykadyskD;
Następnym krokiem było utworzenie „ciała” czołgu czyli jego środkowej części od której potem miałem punkt odniesienia do tworzenia pozostałych części czołgu. Pierwszo funkcją glPushMatrix odkładamy bieżące przekształcenie na stos, by zachować wejściowy stan macierzy widoku modelu. Następnie funkcją glScalef skalujemy nasze ciało czołgu do podanych wymiarów, a potem funkcją glutWireCube rysujemy kwadrat, który będzie przeskalowany. W ten sposób uzyskaliśmy „zawieszenie” czołgu. W następnym kroku wracamy do wejściowej macierzy widoku modelu funkcją glPopMatrix, by móc dokończyć tworzenie ciała poprzez dodanie prostopadłościanu na nasze zawieszenie. Znów odkładamy nasze przekształcenie na stos by móc je wykonać. Nasz prostopadłościan który utworzymy w identyczny sposób przesuwamy funkcją glTranslatef, żeby znalazł się na naszym zawieszeniu. Po utworzeniu ciała czołgu znów wracamy do stanu macierzy widoku modelu wejściowego, aby dokonywać kolejnych przekształceń.
glPushMatrix();
glScalef(10.0, 1.40, 2.0);
glutWireCube(1.0);
glPopMatrix();
glPushMatrix();
glTranslatef(0,1.1,0);
glScalef(10.0, 0.8, 4.0);
glutWireCube(1.0);
glPopMatrix();
Po utworzeniu ciała czołgu bierzemy się za tworzenie kół. Z racji tego, że koła są walcami musimy skorzystać z funkcji gluNewQuadric. Przypisujemy tą funkcje do zmiennych kwadryka i kwadrykadyskD. Następnie tak jak wcześniej odkładamy bieżące przekształcenie na stos. Ustawiamy nasze koło w odpowiednie miejsce funkcją glTranslatef i rysujemy funkcją gluCylinder obramowanie walca w oparciu o wymiary i zdefiniowaną wcześniej zmienną kwadryka. Następnie funkcją gluDisk rysujemy podstawy walca by stworzyć całe koło. Po wykonaniu tej operacji znów wracamy do stanu macierzy widoku modelu wejściowej. Następne koła rysujemy w identyczny sposób przesuwając je tylko w odpowiednie miejsca funkcją glTranslatef. Dlatego też pozwoliłem sobie ominąć opisywanie tworzenia następnych kół.
kwadryka = gluNewQuadric();
kwadrykadyskD = gluNewQuadric();
glPushMatrix();
glTranslatef(-3.5, -0.7, 1.0);
gluCylinder(kwadryka, 1, 1, 1, 12, 3);
gluDisk(kwadrykadyskD,0.0,1,12,3);
glTranslatef(0, 0, 1.0);
gluDisk(kwadrykadyskD,0.0,1,12,3);
glPopMatrix();
gluDeleteQuadric(kwadryka);
Gdy mamy już utworzone ciało i koła czołgu zabieramy się do tworzenia jego wiezyczki i lufy. Tak samo jak przy tworzeniu kół zaczynamy przypisania zmiennej kwadryka funkcji glNewQuadric, oraz odkładamy nasze bieżące przekształcenie na stos. Funkcją translatef wybieramy odpowiednie położenie dla naszej wieżyczki. Następnie funkcją glRotatef zapewniamy, że nasza wieżyczka będzie się obracała. Sam warunek na kąt obrotu jest zapisany przy definiowaniu klawiszy obsługi odpowiednio ustalając zakres możliwości zmiany zmiennej obrotWiezyczki. Wiezyczke tworzymy funkcją gluCylinder ustalając odpowiedni promień wewnętrzny i zewnętrzny, by powstał obły trapez. Po wykonaniu wiezyczki zabieramy się za lufę w podobny sposób najpierw odpowiednio ustawiając miejsce rysowania funkcją glTranslatef, a samą lufę wykonujemy funkcją gluCylinder dobierając odpowiednie promienie. Tak samo jak w przypadku wiezyczki obrót zapewniamy funkcją glRotatef uzależniając ją od zmiennej katlufy ( warunek na kąt także jest zapisany przy obsłudze klawiszy). Projektowanie kończymy jak zawsze powrotem do stanu macierzy widoku modelu wejściowej.
kwadryka = gluNewQuadric();
glPushMatrix();
glTranslatef(1.5, 1.5,0);
glRotatef(obrotWiezyczki, 0, 1, 0);
glRotatef(-90, 1, 0, 0);
gluCylinder(kwadryka, 2.0, 1.0 , 1.1, 10, 3);
gluDeleteQuadric(kwadryka);
kwadryka = gluNewQuadric();
glPushMatrix();
glTranslatef(0, 0, 0.55);
glRotatef(katLufy, 0, 1, 0);
glRotatef(-90, 0, 1, 0);
gluCylinder(kwadryka, 0.15, 0.15, 4.5, 8, 8);
glPopMatrix();
gluDeleteQuadric(kwadryka);
glPopMatrix();
Obsługa klawiszy uzyskujemy wykorzystując funkcje switch - case. Definiujemy, że jeżeli naciśniemy klawisz ‘j’ to nasza wieżyczka obróci się w prawą stronę. Analogicznie robimy to dla klawisza ‘l’ na obrót w stronę lewą. Uzyskujemy ten efekt dzięki inkrementacji wartości obrótWiezyczki o wartość dObrotWiezyczki (obie zadeklarowane są wcześniej w programie) – obrót w prawą stronę, oraz analogiczną dekrementację wartości obrotWiezyczki dla obrotu w lewą stronę. Efekt obrotu bardzo dobrze ilustruje rysunek.
double obrotWiezyczki = 0.0, katLufy = 0.0, dObrotWiezyczki = 3.0,
dKatLufy = 2.0, // wartości 3.0 przy dObrotWiezyczki oraz 2.0 przy dKatLufy oznaczają zakres zmiany kąta odpowiednio o 3 i 2 stopnie
……
switch(klawisz){
……
case 'j':
obrotWiezyczki+=dObrotWiezyczki;
break;
case 'l':
obrotWiezyczki-=dObrotWiezyczki;
break;
Identycznie sprawa ma się w przypadku deklarowania przycisków na kąt wzniesienia lufy. Wartość kat lufy jest albo inkrementowana o wartość dKatlufy ( unoszenie się lufy po naciśnięciu przycisku ‘i’), lub dekrementowana ( opadanie lufy po naciśnięciu przycisku ‘k’). W przypadku tych klawiszy musimy narzucić na nie warunki by lufa nie opadała poniżej kąta 0 stopni ( warunek: katLufy >= 0.0) lub nie wznosiła się powyżej kąta 60 stopni (warunek: katLufy < 60.0). Realizację przedstawia rysunek.
case 'k':
if (katLufy >= 0.0)
katLufy-=dKatLufy;
break;
case 'i':
if (katLufy < 60.0)
katLufy+=dKatLufy;
break;
W przypadku definiowania obsługi kalwiszy mających na celu przybliżanie, oddalanie oraz zmiane położenia obserwatora względem osi OX, OY, OZ także posłuży na funkcja switch-case.
Wyniki
Otrzymaliśmy w ten sposób trójwymiarowy czołg z możliwością obrotu jego wierzyczki o 360 stopni w kierunku poziomym oraz lufy o 60 stopni w kierunku pionowym. Za pomocą odpowiednich przycisków możemy także zmieniać położenie obserwatora. O to zdjęcie wyniku naszego projektu:
Kod programu
#include <math.h>
#include <string.h>
#include <stdio.h>
#include <GL/glut.h>
/* Definicja stalych */
#define DLUGOSC_BOKU 5.0
#define OBSERWATOR_ODLEGLOSC 50.0
#define OBSERWATOR_OBROT_X 0.0
#define OBSERWATOR_OBROT_Y 0.0
#define OBSERWATOR_OBROT_Z 0.0
#define OBSERWATOR_FOV_Y 30.0
#define pi 3.1415
////////////////////////////////////////////////////////////////////////////////////////////
/* Zmienne globalne*/
double bok = DLUGOSC_BOKU; // Długosc boku szescianu
int szerokoscOkna = 800;
int wysokoscOkna = 600;
float lpodz = 25.0;
GLfloat Range = OBSERWATOR_ODLEGLOSC;
/* Katy obrotu figury */
GLfloat rotatex = OBSERWATOR_OBROT_X ;
GLfloat rotatey = OBSERWATOR_OBROT_Y;
GLfloat rotatez = OBSERWATOR_OBROT_Z;
GLfloat
angle1 = 1,
angle2 = 1,
angle3 = 1;
/* Wpółrzędne położenia obserwatora */
GLdouble eyex = 0;
GLdouble eyey = 0;
GLdouble eyez = 0;
/* Współrzędne punktu w którego kierunku jest zwrócony obserwator*/
GLdouble centerx = 0;
GLdouble centery = 0;
GLdouble centerz = -100;
/* Skladowe koloru */
GLfloat red = 1.0, green = 1.0, blue = 1.0;
double obrotWiezyczki = 0.0, katLufy = 0.0, dObrotWiezyczki = 3.0,
dKatLufy = 2.0, alfa=0, beta=0;
/* Prototypy funkcji */
void Rysuj();
void UstawParametryWidoku(int szer, int wys);
void WyswietlObraz(void);
void ObslugaKlawiatury(unsigned char klawisz, int x, int y);
void DrawString (GLfloat x, GLfloat y, char *string);
void ShowInstructions(bool Test);
//////////////////////////////////////////////////////////////////////////////////////////
/*Funkcja rysująca*/
void Rysuj()
{
GLUquadricObj *kwadryka;
GLUquadricObj *kwadrykadyskD;
glColor3f (0.0,0.0,0.0);
//cialo
glPushMatrix();
glScalef(10.0, 1.40, 2.0);
glutWireCube(1.0);
glPopMatrix();
glPushMatrix();
glTranslatef(0,1.1,0);
glScalef(10.0, 0.8, 4.0);
glutWireCube(1.0);
glPopMatrix();
//kola przednie
kwadryka = gluNewQuadric();
kwadrykadyskD = gluNewQuadric();
glPushMatrix();
glTranslatef(-3.5, -0.7, 1.0);
gluCylinder(kwadryka, 1, 1, 1, 12, 3);
gluDisk(kwadrykadyskD,0.0,1,12,3);
glTranslatef(0, 0, 1.0);
gluDisk(kwadrykadyskD,0.0,1,12,3);
glPopMatrix();
gluDeleteQuadric(kwadryka);
kwadryka = gluNewQuadric();
kwadrykadyskD = gluNewQuadric();
glPushMatrix();
glTranslatef(-3.5, -0.7, -2.0);
gluCylinder(kwadryka, 1, 1, 1, 12, 3);
gluDisk(kwadrykadyskD,0.0,1,12,3);
glTranslatef(0, 0, 1.0);
gluDisk(kwadrykadyskD,0.0,1,12,3);
glPopMatrix();
gluDeleteQuadric(kwadryka);
//kola tylne
kwadryka = gluNewQuadric();
kwadryka = gluNewQuadric();
kwadrykadyskD = gluNewQuadric();
glPushMatrix();
glTranslatef(3.5, -0.7, 1.0);
gluCylinder(kwadryka, 1, 1, 1, 12, 3);
gluDisk(kwadrykadyskD,0.0,1,12,3);
glTranslatef(0, 0, 1.0);
gluDisk(kwadrykadyskD,0.0,1,12,3);
glPopMatrix();
gluDeleteQuadric(kwadryka);
kwadryka = gluNewQuadric();
kwadrykadyskD = gluNewQuadric();
glPushMatrix();
glTranslatef(3.5, -0.7, -2.0);
gluCylinder(kwadryka, 1, 1, 1, 12, 3);
gluDisk(kwadrykadyskD,0.0,1,12,3);
glTranslatef(0, 0, 1.0);
gluDisk(kwadrykadyskD,0.0,1,12,3);
glPopMatrix();
gluDeleteQuadric(kwadryka);
//wieza z lufą
kwadryka = gluNewQuadric();
glPushMatrix();
glTranslatef(1.5, 1.5,0);
glRotatef(obrotWiezyczki, 0, 1, 0);
glRotatef(-90, 1, 0, 0);
gluCylinder(kwadryka, 2.0, 1.0 , 1.1, 10, 3);
gluDeleteQuadric(kwadryka);
kwadryka = gluNewQuadric();
glPushMatrix();
glTranslatef(0, 0, 0.55);
glRotatef(katLufy, 0, 1, 0);
glRotatef(-90, 0, 1, 0);
gluCylinder(kwadryka, 0.15, 0.15, 4.5, 8, 8);
glPopMatrix();
gluDeleteQuadric(kwadryka);
glPopMatrix();
}
//////////////////////////////////////////////////////////////////////////////////////////
/* Funkcja ustawiajaca parametry rzutu perspektywicznego i rozmiary viewportu */
void UstawParametryWidoku(int szer, int wys)
{
// Zapamietanie wielkosci widoku
szerokoscOkna = szer;
wysokoscOkna = wys;
// Ustawienie parametrow viewportu
glViewport(0, 0, szerokoscOkna, wysokoscOkna);
// Przejscie w tryb modyfikacji macierzy rzutowania
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(OBSERWATOR_FOV_Y,
(float)szerokoscOkna/(float)wysokoscOkna, 1.0, 1000.0);
/* Zakomentowac od tad do wypelnien prymitywow */
// Przejscie w tryb modyfikacji macierzy przeksztalcen geometrycznych
glMatrixMode(GL_MODELVIEW);
// Zmiana macierzy znajdujacej sie na wierzcholku stosu na macierz jednostkowa
glLoadIdentity();
// Wyswietlanie wielokatow w postaci obrysow.
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
}
//////////////////////////////////////////////////////////////////////////////////////////
/* Funkcja generujaca pojedyncza klatke animacji */
void WyswietlObraz(void)
{
double r;
// Wyczyszczenie bufora ramki i bufora glebokosci
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
// Okreslenie wielkosci widoku, wlaczenie rzutowania perspektywicznego
// i zainicjowanie stosu macierzy modelowania
UstawParametryWidoku(szerokoscOkna, wysokoscOkna);
//r=Range*cos(beta*pi/180);
//gluLookAt(r*sin(alfa*pi/180), Range*sin(beta*pi/180),
(r*sin(alfa*pi/180), 0, 0, 0, 0, 1, 0);
//gluLookAt(alfa, beta, alfa, 0, 0, 0, 0, 1, 0);
// Ustalenie polozenia obserwatora
glTranslatef(0, 0, -Range);
glRotatef(rotatex, 1, 0, 0);
glRotatef(rotatey, 0, 1, 0);
glRotatef(rotatez, 0, 0, 1);
// Generacja obrazu:
Rysuj();
// Przelaczenie buforow ramki
glutSwapBuffers();
}
//////////////////////////////////////////////////////////////////////////////////////////
/* Funkcja obslugi klawiatury */
void ObslugaKlawiatury(unsigned char klawisz, int x, int y)
{
switch(klawisz)
{
case 'j':
obrotWiezyczki+=dObrotWiezyczki;
break;
case 'l':
obrotWiezyczki-=dObrotWiezyczki;
break;
case 'k':
if (katLufy >= 0.0)
katLufy-=dKatLufy;
break;
case 'i':
if (katLufy < 60.0)
katLufy+=dKatLufy;
break;
case 'x':
rotatex ++ ;
break;
case 's':
rotatex -- ;
break;
case 'c':
rotatey ++ ;
break;
case 'd':
rotatey -- ;
break;
case 'z':
rotatez ++ ;
break;
case 'a':
rotatez -- ;
break;
case 'o':
Range ++ ;
break;
case 'p':
Range -- ;
break;
case 27:
exit(0);
break;
}
}
///////////////////////////////////////////////////////////////////////////////////////////
/* Funkcja wyswietlajaca napis */
void DrawString (GLfloat x, GLfloat y, char *string)
{
// położenie napisu
glRasterPos2f (x,y);
// wyświetlenie napisu
int len = strlen (string);
for (int i = 0; i < len; i++)
glutBitmapCharacter (GLUT_BITMAP_9_BY_15,string [i]);
}
//////////////////////////////////////////////////////////////////////////////////////////
/* Funkcja wyswietlajaca instrukcje */
void ShowInstructions(bool Test)
{
if(Test)
{
char napis[] = "Kursor gora\\doł - X.\n";
char napis2[] = "Kursor lewo\\prawo - Y.";
char napis3[] = "Page Up\\Page Down - Z.";
DrawString(0, 0.1, napis);
DrawString(0, 0.5, napis2);
DrawString(0, 55, napis2);
}
}
//////////////////////////////////////////////////////////////////////////////////////////
/* Glowna funkcja programu */
int main(int argc, char **argv)
{
// Zainicjowanie biblioteki GLUT
glutInit(&argc, argv);
// Ustawienie trybu wyswietlania
glutInitDisplayMode (GLUT_DOUBLE|GLUT_RGBA|GLUT_DEPTH);
// Ustawienie polozenia dolenego lewego rogu okna
glutInitWindowPosition(100, 100);
// Ustawienie rozmiarow okna
glutInitWindowSize(szerokoscOkna, wysokoscOkna);
// Utworzenie okna
glutCreateWindow("LAB_2");
// Odblokowanie bufora glebokosci
glEnable(GL_DEPTH_TEST);
// Ustawienie funkcji wykonywanej na danych w buforze glebokosci
glDepthFunc(GL_LEQUAL);
// Ustawienie wartosci czyszczacej zawartosc bufora glebokosci
glClearDepth(1000.0);
// Ustawienie koloru czyszczenia bufora ramki
glClearColor (1.0f, 1.0f, 1.0f, 1.0f);
// Zarejestrowanie funkcji (callback) wyswietlajacej
glutDisplayFunc(WyswietlObraz);
// Zarejestrowanie funkcji (callback) wywolywanej za kazdym razem kiedy
// zmieniane sa rozmiary okna
glutReshapeFunc(UstawParametryWidoku);
// Zarejestrowanie funkcji wykonywanej gdy okno nie obsluguje
// zadnych zadan
glutIdleFunc(WyswietlObraz);
// Zarejestrowanie funkcji obslugi klawiatury
glutKeyboardFunc(ObslugaKlawiatury);
// Obsluga glownej petli programu (wywolywanie zarejestrowanych callbackow
// w odpowiedzi na odbierane zdarzenia lub obsluga stanu bezczynnosci)
glutMainLoop();
return 0;
}
Wnioski
Program LOGO!Soft pozwala na wygodne projektowanie układów sterowani. Praca w nim była bardzo kształcąca, ponieważ pozwala nam na zaprogramowanie sterownika od podstaw. Należy jednak uważać, ponieważ bramki AND mają 3 wejścia i trzeba dokładnie wypełnić wszystkie pola. Jednak nawet gdy się o tym zapomnimy program nie dopuści do zakończenia pracy bez uzupełnienia wymaganych pól.