Temat indywidualnego zadania laboratoryjnego
Algorytm rozwiązania zadania - rysowanie zadanego obiektu
- kod programu
void InicjalizujWyrzutnie(void)
{
podstawa = gluNewQuadric();
gluQuadricDrawStyle(podstawa, GLU_LINE);
podstawaDyskGorny = gluNewQuadric();
gluQuadricDrawStyle(podstawaDyskGorny, GLU_LINE);
podstawaDyskDolny = gluNewQuadric();
gluQuadricDrawStyle(podstawaDyskDolny, GLU_LINE);
slup = gluNewQuadric();
gluQuadricDrawStyle(slup, GLU_LINE);
wyrzutniaPrawa = gluNewQuadric();
gluQuadricDrawStyle(wyrzutniaPrawa, GLU_LINE);
wyrzutniaPrawaDysk = gluNewQuadric();
gluQuadricDrawStyle(wyrzutniaPrawaDysk, GLU_LINE);
wyrzutniaLewaDysk = gluNewQuadric();
gluQuadricDrawStyle(wyrzutniaLewaDysk, GLU_LINE);
wyrzutniaLewa = gluNewQuadric();
gluQuadricDrawStyle(wyrzutniaLewa, GLU_LINE);
radar = gluNewQuadric();
gluQuadricDrawStyle(radar, GLU_LINE);
}
void RysujWyrzutnie(GLfloat obrotWyrzutniPion, GLfloat obrotWyrzutniPoz)
{
glColor3f(1.0,1.0,1.0);
glPushMatrix();
glPushMatrix();
glRotatef(-90.0, 1, 0, 0);
gluCylinder(podstawa, 3.0, 3.0, 1.0, 20, 4);
glTranslatef(0.0, 0.0, 1.0);
gluDisk(podstawaDyskGorny, 0.0, 3.0, 20, 4);
// - slup
gluCylinder(slup, 0.75, 0.75, 3.0, 20, 4);
glPopMatrix();
// belki w wyrzutniami
glPushMatrix();
glTranslatef(0.0, 1.0+3.0+0.5, 0.0);
glRotatef(obrotWyrzutniPoz, 0, 1, 0);
glRotatef(obrotWyrzutniPion, 1, 0, 0);
// - radar
gluSphere(radar, 0.75, 10, 10);
// - wyrzutnia prawa
glTranslatef(2.0, -1.0, -1.5);
gluDisk(wyrzutniaPrawaDysk, 0.0, 0.5, 20, 1);
gluCylinder(wyrzutniaPrawa, 0.5, 0.5, 4.0, 20, 4);
// - wyrzutnia lewa
glTranslatef(-4.0, 0.0, 0.0);
gluDisk(wyrzutniaLewaDysk, 0.0, 0.5, 20, 1);
gluCylinder(wyrzutniaPrawa, 0.5, 0.5, 4.0, 20, 4);
// - belka
glTranslatef(2.0, 0.5+0.125, 1.5);
glScalef(4.0, 0.25, 1.25);
glutWireCube(1);
glPopMatrix();
// Posprzatanie na stosie macierzy modelowania
glPopMatrix();
}
opis funkcjonowania algorytmu
Narysowanie wyrzutni na ekranie jest wynikiem działania dwóch wyżej zamieszczonych funkcji. Pierwsza z nich jest odpowiedzialna na zainicjalizowanie wartości zmiennych wskaźnikowych zadeklarowanych globalnie. Są to wskaźniki do kwadryk:
GLUquadricObj *podstawa;
GLUquadricObj *podstawaDyskGorny;
GLUquadricObj *podstawaDyskDolny;
GLUquadricObj *slup;
GLUquadricObj *wyrzutniaPrawa;
GLUquadricObj *wyrzutniaPrawaDysk;
GLUquadricObj *wyrzutniaLewaDysk;
GLUquadricObj *wyrzutniaLewa;
GLUquadricObj *radar;
Każda z powyższych zmiennych jest inicjalizowana zgodnie z poniższym schematem:
- rezerwacja pamięci:
podstawa = gluNewQuadric();
- określenie typu kwadryki (u mnie linie GLU_LINE)
gluQuadricDrawStyle(podstawa, GLU_LINE);
Wynikiem działania funkcji są obiekty kwadryk liniowych przechowujących dane o rysowanych obiektach, na których bazuje rysując wyrzutnię rakiet.
Dalszym krokiem w kierunki realizacji zadania laboratoryjnego jest druga z zamieszczonych powyżej procedur. W oparciu o prymitywy przestrzenne i utworzone wcześniej kwadryki rysuje ona zadany obiekt.
W celu do poprawnego działania do procedury należy przekazać dwa parametry, które będą interpretowane zgodnie z poniższym schematem:
GLfloat obrotWyrzutniPion - kąt odchylenia wieżyczki wyrzutni do położenia pierwotnego w płaszczyźnie pionowej
GLfloat obrotWyrzutniPoz - kąt odchylenia wieżyczki wyrzutni od położenia pierwotnego w płaszczyźnie poziomej.
Na początku procedura przygotowuje macierz punktów dla obiektów, które nie ulegają rotacji. Są to podstawa oraz słup wyrzutni.
glPushMatrix();
glRotatef(-90.0, 1, 0, 0);
gluCylinder(podstawa, 3.0, 3.0, 1.0, 20, 4);
glTranslatef(0.0, 0.0, 1.0);
gluDisk(podstawaDyskGorny, 0.0, 3.0, 20, 4);
// - slup
gluCylinder(slup, 0.75, 0.75, 3.0, 20, 4);
glPopMatrix();
Dokonuje rotacji w celu zmiany kierunku rysowania walca stanowiącego postawę. Funkcją, którą wykorzystuje [gluCylinder(...)] rysuje wysokość walca w kierunku dodatniej osi Z. Zatem rotacja o 90° powoduje dla obserwatora zmianę osi Y z Z. W wyniku otrzymuje walec rysowany w pionie. Następnie następuje przesunięcie środka układu współrzędnych o wektor - glTranslatef(x, y, z), ponieważ walec rysowany jest od środka dolnej podstawy. Zatem zmuszony jestem o przemieszczenie się w pionie o wysokość podstawy w celu narysowania słupa. W wyniku działania tego fragmentu kodu otrzymuje:
Druga macierz zawiera punkty, które wg treści zadania, mają ulegać rotacjom.
glPushMatrix();
glTranslatef(0.0, 1.0+3.0+0.5, 0.0);
glRotatef(obrotWyrzutniPoz, 0, 1, 0);
glRotatef(obrotWyrzutniPion, 1, 0, 0);
// - radar
gluSphere(radar, 0.75, 10, 10);
// - wyrzutnia prawa
glTranslatef(2.0, -1.0, -1.5);
gluDisk(wyrzutniaPrawaDysk, 0.0, 0.5, 20, 1);
gluCylinder(wyrzutniaPrawa, 0.5, 0.5, 4.0, 20, 4);
// - wyrzutnia lewa
glTranslatef(-4.0, 0.0, 0.0);
gluDisk(wyrzutniaLewaDysk, 0.0, 0.5, 20, 1);
gluCylinder(wyrzutniaPrawa, 0.5, 0.5, 4.0, 20, 4);
// - belka
glTranslatef(2.0, 0.5+0.125, 1.5);
glScalef(4.0, 0.25, 1.25);
glutWireCube(1);
glPopMatrix();
Wstępnie dokonywana jest rotacja układu współrzędnych w pionie i poziomie zgodnie z wartościami zmiennych przekazywanych do procedury.
glRotatef(obrotWyrzutniPoz, 0, 1, 0);
glRotatef(obrotWyrzutniPion, 1, 0, 0);
W celu wykonania poprawnego obrotu środek układu współrzędnych (przed wykonaniem rotacji) zostaje przesunięty (translowany) do punktu [0.0; 4.5; 0.0] - jest to środek kuli wyrzutni.
glTranslatef(0.0, 1.0+3.0+0.5, 0.0);
Wg tego punktu dokonywane są rotacje.
Dalej przystępuje na rysowania wieżyczki wyrzutni począwszy od sfery:
gluSphere(radar, 0.75, 10, 10);
gdzie:
radar: kwadryka
0.75: promień sfery
10, 10: ilość podziałów pionowych i poziomych
Kolejnym etapem jest rysowanie wyrzutni rakiet. Przesuwam się od środka sfery do punktu, gdzie będzie się znajdował środek podstawy walca imitującego wyrzutnie:
glTranslatef(2.0, -1.0, -1.5);
Następnie rysuje dysk, który będzie stanowił zamknięcie wyrzutni (podstawę walca):
gluDisk(wyrzutniaPrawaDysk, 0.0, 0.5, 20, 1);
i sam walec:
gluCylinder(wyrzutniaPrawa, 0.5, 0.5, 4.0, 20, 4);
Sąsiednia, 2 wyrzutnia jest rysowana zgodnie z powyższym schematem. Różnicą jest tylko wektor przesunięcia. Przemieszczam się nie od środka kuli, a środka dysku zamykającego wyrzutnie nr 1. Kod wygląda następująco:
glTranslatef(-4.0, 0.0, 0.0);
gluDisk(wyrzutniaLewaDysk, 0.0, 0.5, 20, 1);
gluCylinder(wyrzutniaPrawa, 0.5, 0.5, 4.0, 20, 4);
a efekt jego działania:
Ostatnim etapem rysowania wieżyczki jest belka łącząca wszystkie 3, wyżej rysowane, obiekty. Rysuję ją na końcu, ponieważ wykorzystuje w tym celu funkcję rysującą sześcian, której jako parametr podaje długość boku:
glutWireCube(1);
W celu otrzymania prostopadłościanu dokonuje przeskalowania osi przed narysowaniem sześcianu:
glScalef(4.0, 0.25, 1.25);
W wyniku czego każdy rysowany punkt sześcianu, a dokładniej jego współrzędne są przemnażane przed wartość przeskalowania (dla odpowiedniej osi). W ten sposób otrzymuje kompletną wieżyczkę, która wygląda następująco:
Natomiast kompletny, zamodelowany przeze mnie obiekt prezentuje się następująco:
Algorytm rozwiązania zadania - zmiana położenia obserwatora
kod programu
glTranslatef(0,0, -sqrt(odleglosc*odleglosc-wysokosc*wysokosc));
katX=(float)(asin(wysokosc/odleglosc)*180/PI);
glRotatef(katX, 1, 0, 0);
glRotatef(rotObsY,0,1,0);
opis funkcjonowania algorytmu
Wyżej zamieszczony kod jest fragmentem funkcji odpowiedzialnej za wygenerowanie pojedynczej klatki animacji. Bezpośrednio po nim następuje wywołanie funkcji odpowiadającej za narysowanie wyrzutni (omówiona powyżej).
Działanie algorytmu opiera się o schemat:
Wstępnie następuje odsunięcie obserwatora od środka układu współrzędnych, gdzie rysowana jest wyrzutnia. Odsunięcie, po osi Z, wyznaczam z twierdzenia Pitagorasa i dokonuje translacji:
glTranslatef(0,0, -sqrt(odleglosc*odleglosc-wysokosc*wysokosc));
Następnie, zgodnie z powyższym schematem, wyznaczany jest kąt o jaki zostanie podniesiony obserwator - kąt rotacji względem osi X:
Ponieważ funkcje trygonometryczne wracają wartość w radianach, dlatego dokonuje zamiany wartości na stopnie:
O tak uzyskany kąt dokonuje rotacji względem osi X:
glRotatef(katX, 1, 0, 0);
A następnie wykonuje obrót obserwatora wokół obiektu - rotacje względem osi Y:
glRotatef(rotObsY,0,1,0);
Zmienna rotObsY przyjmuje wartości 0°-360° co umożliwia pełny obrót obserwatora wokół obiektu.
Kod źródłowy programu
#include <GL/glut.h>
#include <math.h>
#define PI 3.141592653
// Wskazniki do wykorzystywanych kwadryk
GLUquadricObj *podstawa;
GLUquadricObj *podstawaDyskGorny;
GLUquadricObj *podstawaDyskDolny;
GLUquadricObj *slup;
GLUquadricObj *wyrzutniaPrawa;
GLUquadricObj *wyrzutniaPrawaDysk;
GLUquadricObj *wyrzutniaLewaDysk;
GLUquadricObj *wyrzutniaLewa;
GLUquadricObj *radar;
GLfloat rotObsY = 40.0;
GLfloat wysokosc = 15.0;
GLfloat odleglosc = 30.0;
GLfloat katX;
GLfloat rotPoz = 0.0;
GLfloat rotPion= 0.0;
void InicjujWyrzutnie(void)
{
podstawa = gluNewQuadric();
gluQuadricDrawStyle(podstawa, GLU_LINE);
podstawaDyskGorny = gluNewQuadric();
gluQuadricDrawStyle(podstawaDyskGorny, GLU_LINE);
podstawaDyskDolny = gluNewQuadric();
gluQuadricDrawStyle(podstawaDyskDolny, GLU_LINE);
slup = gluNewQuadric();
gluQuadricDrawStyle(slup, GLU_LINE);
wyrzutniaPrawa = gluNewQuadric();
gluQuadricDrawStyle(wyrzutniaPrawa, GLU_LINE);
wyrzutniaPrawaDysk = gluNewQuadric();
gluQuadricDrawStyle(wyrzutniaPrawaDysk, GLU_LINE);
wyrzutniaLewaDysk = gluNewQuadric();
gluQuadricDrawStyle(wyrzutniaLewaDysk, GLU_LINE);
wyrzutniaLewa = gluNewQuadric();
gluQuadricDrawStyle(wyrzutniaLewa, GLU_LINE);
radar = gluNewQuadric();
gluQuadricDrawStyle(radar, GLU_LINE);
}
void RysujWyrzutnie(GLfloat obrotWyrzutniPion, GLfloat obrotWyrzutniPoz)
{
glColor3f(0.0,0.0,0.0);
// Przygotowanie stosu macierzy modelowania
glPushMatrix();
// Rysowanie podstawy i slupa
glPushMatrix();
// - sciany boczne
glRotatef(-90.0, 1, 0, 0);
gluCylinder(podstawa, 3.0, 3.0, 1.0, 20, 4);
// - gorna podstawa
glTranslatef(0.0, 0.0, 1.0);
gluDisk(podstawaDyskGorny, 0.0, 3.0, 20, 4);
// - slup
gluCylinder(slup, 0.75, 0.75, 3.0, 20, 4);
glPopMatrix();
// belki w wyrzutniami
glPushMatrix();
glTranslatef(0.0, 1.0+3.0+0.5, 0.0);
glRotatef(obrotWyrzutniPoz, 0, 1, 0);
glRotatef(obrotWyrzutniPion, 1, 0, 0);
// - radar
gluSphere(radar, 0.75, 10, 10);
// - wyrzutnia prawa
glTranslatef(2.0, -1.0, -1.5);
gluDisk(wyrzutniaPrawaDysk, 0.0, 0.5, 20, 1);
gluCylinder(wyrzutniaPrawa, 0.5, 0.5, 4.0, 20, 4);
// - wyrzutnia lewa
glTranslatef(-4.0, 0.0, 0.0);
gluDisk(wyrzutniaLewaDysk, 0.0, 0.5, 20, 1);
gluCylinder(wyrzutniaPrawa, 0.5, 0.5, 4.0, 20, 4);
// - belka
glTranslatef(2.0, 0.5+0.125, 1.5);
glScalef(4.0, 0.25, 1.25);
glutWireCube(1);
glPopMatrix();
// Posprzatanie na stosie macierzy modelowania
glPopMatrix();
}
void WyswietlObraz(void)
{
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
glPushMatrix();
glTranslatef(0,0, -odleglosc);
katX=(float)(asin(wysokosc/odleglosc)*180/PI);
glRotatef(katX, 1, 0, 0);
glRotatef(rotObsY,0,1,0);
RysujWyrzutnie(rotPion, rotPoz);
glPopMatrix();
glutSwapBuffers();
}
void UstawParametryWidoku(int szerokosc, int wysokosc)
{
glViewport(0, 0, szerokosc, wysokosc);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(40.0, (float)szerokosc/(float)wysokosc, 1.0, 1000.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void ObslugaKlawiszySpecjalnych(int klawisz, int x, int y)
{
switch(klawisz)
{
case GLUT_KEY_UP:
wysokosc = (wysokosc < odleglosc) ? wysokosc + 1.0 : wysokosc;
break;
case GLUT_KEY_DOWN:
wysokosc = (wysokosc > 0.0) ? wysokosc - 1.0 : wysokosc;
break;
case GLUT_KEY_LEFT:
rotObsY = (rotObsY > 0.0) ? rotObsY - 1.0 : 360.0;
break;
case GLUT_KEY_RIGHT:
rotObsY = (rotObsY < 360.0) ? rotObsY + 1.0 : 0.0;
break;
}
}
void ObslugaKlawiatury(unsigned char klawisz, int x, int y)
{
switch(klawisz)
{
case '1':
rotPion = (rotPion < 50.0) ? rotPion + 1.0 : rotPion; break;
case '!':
rotPion = (rotPion > -50.0) ? rotPion - 1.0 : rotPion; break;
case '2':
rotPoz = (rotPoz < 360.0) ? rotPoz + 1.0 : 0.0; break;
case '@':
rotPoz = (rotPoz > 0.0) ? rotPoz - 1.0 : 360.0; break;
case '3':
odleglosc = (odleglosc < 60.0) ? odleglosc + 1.0 : odleglosc; break;
case '#':
odleglosc = (wysokosc/odleglosc < 1) ? odleglosc - 1.0 : odleglosc; break;
}
if(klawisz == 27)
exit(0);
}
int main(int argc, char **argv)
{
glutInit(&argc, argv);
glutInitDisplayMode (GLUT_DOUBLE|GLUT_RGB|GLUT_DEPTH);
glutInitWindowPosition(100, 100);
glutInitWindowSize(400, 400);
glutCreateWindow("Wieza z klockow");
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glClearDepth(1000.0);
glClearColor (1.0, 1.0, 1.0, 1.0);
glutDisplayFunc(WyswietlObraz);
glutReshapeFunc(UstawParametryWidoku);
glutIdleFunc(WyswietlObraz);
glutKeyboardFunc(ObslugaKlawiatury);
glutSpecialFunc(ObslugaKlawiszySpecjalnych);
InicjujWyrzutnie();
glutMainLoop();
return 0;
}
7
Copyright ©2005 Piotr Barankiewicz