Sprawozdanie z ćwiczeń laboratoryjnych z przedmiotu Grafika komputerowa
Imię i nazwisko: |
Grupa szkoleniowa: |
Data:
|
Prowadzący zajęcia: |
|
Andrzej Sobolewski |
I7X6S1 |
19.11.2008 |
dr inż. Marek Salamon |
|
Zestaw: |
Ocena: |
|||
20 |
|
|||
|
|
1. Treść zadania:
Wykorzystując wybrane funkcje modelowania biblioteki OpenGL i Glut napisać program przedstawiający perspektywistyczny obraz bryły 3D o zadanych parametrach
- podstawy bryły zamodelować w trybie GL_TRIANGLE_FAN
- w przypadku wycinków bryły powierzchnie wewnętrzną zamodelować w dowolnym trybie,
- wprowadzić możliwość zmiany liczby podziałów pionowych i poziomych bryły z zakresie o 5 do 20,
- wprowadzić możliwość zmiany obserwatora.
20. 1/4 sfery w trybie GL_TRIANGLE_STRIP o promieniu 1 i środku w punkcie (0, 0, 0).
2. Algorytm rozwiązania zadania na podstawie kodu programu (funkcji) odpowiedzialnego za narysowanie na ekranie zadanego obiektu:
void RysujSzescian(double r)
{
for(y=-1.57;y<1.57;y+=delta)
{
for(x=1.57-delta;x<=3.14;x+=delta)
{
glBegin(GL_TRIANGLE_STRIP);
glVertex3f(r*cos(y)*cos(x) , r*sin(y) , r*cos(y)*sin(x));
glVertex3f(r*cos(y)*cos(x+delta) , r*sin(y) , r*cos(y)*sin(x+delta));
glVertex3f(r*cos(y+delta)*cos(x+delta) , r*sin(y+delta) , r*cos(y+delta)*sin(x+delta));
glVertex3f(r*cos(y+delta)*cos(x) , r*sin(y+delta) , r*cos(y+delta)*sin(x));
glEnd();
}
}
for(y=-1.57;y<1.57;y+=delta)
{
for(x=3,14;x<4.71;x+=delta)
{
glBegin(GL_TRIANGLE_STRIP);
glVertex3f(0 , r*sin(y) , -r*cos(y)*sin(x));
glVertex3f(0 , r*sin(y) , -r*cos(y)*sin(x+delta));
glVertex3f(0 , r*sin(y+delta) , -r*cos(y+delta)*sin(x+delta));
glVertex3f(0 , r*sin(y+delta) , -r*cos(y+delta)*sin(x));
glEnd();
}
}
for(y=-1.57;y<1.57;y+=delta)
{
for(x=3,14;x<4.71;x+=delta)
{
glBegin(GL_TRIANGLE_STRIP);
glVertex3f(r*cos(y)*cos(x) , r*sin(y) , 0);
glVertex3f(r*cos(y)*cos(x+delta) , r*sin(y) , 0);
glVertex3f(r*cos(y+delta)*cos(x+delta) , r*sin(y+delta) , 0);
glVertex3f(r*cos(y+delta)*cos(x) , r*sin(y+delta) , 0);
glEnd();
}
}
}
3. Opis działania funkcji:
Funkcja RysujSzescian posiada jeden argument wejściowy. Jest to zmienna r typu double, której wartość możemy zmieniać w kodzie programu „szescian.c”, regulując w ten sposób promień zadanej sfery. Algorytm rozpoczyna się od dwóch zagnieżdżonych pętli for, które są odpowiedzialne za rysowanie powłoki sfery. Zmienne x, y oraz r używane w tych pętlach są potrzebne do określania położenia punktu w przestrzeni. Pętle for zawierają parametry zmiany kąta rysowania. Zmienna delta służy do zmieniania liczby podziałów pionowych i poziomych bryły. Wyrażenie glBegin(GL_TRIANGLE_STRIP); rozpoczyna blok, w którym tworzona jest powierzchnia w zadanym trybie. Tryb GL_TRIANGLE_STRIP polega na definiowaniu połączonych ze sobą trójkątów w taki sposób, że pierwszy tworzą wierzchołki p1, p2, p3, drugi p3, p2, p4, …, ostatni pn-1, pn-2m pn. Tutaj w każdej iteracji łączymy ze sobą cztery kolejne punkty (wierzchołki trójwymiarowe o współrzędnych typu GLfloat(x, y, z)), które są zdefiniowane za pomocą wyrażenia glVertex3f();. Funkcja ta pochodzi z biblioteki OpenGL, a wartościami przekazywanymi do niej są współrzędne x, y, z w układzie kartezjańskim, którym nadaję odpowiednie wartości za pomocą funkcji sinus oraz cosinus odpowiednio pomnożonych przez siebie oraz promień.
Blok tworzący powłokę sfery zamyka funkcja glEnd();. Następnie w podwójnie zagnieżdzonych pętlach for tworzone są po kolei powierzchnie wewnętrzne bryły, wycinające ¼ sfery. Działają one w podobny sposób. Różnią się tylko parametrami oraz ich zmianami określającymi współrzędne wierzchołków trójwymiarowych w funkcji glVertex3f();, opisanej powyżej. Realizują rysowanie półokręgów. Ponadto do wyznaczenia powyższych współrzędnych wykorzystuje się zależności wynikające z funkcji trygonometrycznych kątów. Należy tu pamiętać o zamianie wartości kątów ze stopni na radiany, a także o fakcie iż tylko dwie współrzędne ulegają zmianie. Podobnie jak w przypadku sfery, glBegin(GL_TRIANGLE_STRIP); rozpoczyna blok, w którym tworzona jest powierzchnia w zadanym trybie, a glEnd(); kończy ten blok. Algorytm działania pętli jest następujący - jednorazowy obieg jest odpowiedzialny za narysowanie wycinka powierzchni związanego z jednym przedziałem pionowym. W tym celu określana jest długość promienia potrzebna do narysowania okręgów stanowiących ograniczenia przedziału. Następnie zagnieżdżona pętla odpowiada za narysowanie punktów przestrzennych na okręgach o wyżej obliczonych promieniach.
Wprowadzenie możliwości zmiany obserwatora:
Kod realizujący powyższą czynność wygląda następująco:
int odl=OBSERWATOR_ODLEGLOSC;
int obr_x=OBSERWATOR_OBROT_X;
int obr_y=OBSERWATOR_OBROT_Y;
void ObslugaKlawiatury(unsigned char klawisz, int x, int y)
{
if(klawisz == '+')
r *= 2.0;
else if (klawisz == '-')
r /= 2.0;
else if (klawisz == 'p')
{
if (delta<0.5) delta=delta+0.01;
else if (delta =0.5) delta=0.2;
}
else if (klawisz == 'o')
{
if (delta>0.155) delta=delta-0.01;
else if (delta =0.155) delta=0.5;
}
else if (klawisz == 's') odl=odl+2;
else if (klawisz == 'w') odl=odl-2;
else if (klawisz == 'a') obr_x=obr_x+2;
else if (klawisz == 'd') obr_x=obr_x-2;
else if (klawisz == 'q') obr_y=obr_y+2;
else if (klawisz == 'e') obr_y=obr_y-2;
else if (klawisz == 27)
exit(0);
}
Klawisze obsługi są następujące:
-'a' oraz `d' odpowiadają za obrót w osi OX,
-`q' oraz `e' odpowiadają za obrót w osi OY,
-`w' za przybliżenie, `s' za oddalenie,
-`o` za zwiększanie liczby podziałów pionowych i poziomych bryły,
-`p' za zmniejszanie liczby podziałów pionowych i poziomych bryły.
Zmienne:
int odl=OBSERWATOR_ODLEGLOSC;
int obr_x=OBSERWATOR_OBROT_X;
int obr_y=OBSERWATOR_OBROT_Y;
zostały zdefiniowane globalnie wartościami na początku programu. Po naciśnięciu danego klawisza, wartości odpowiadające położeniu obserwatora zostają zmienione zgodnie z przypisaniami zawartymi w instrukcjach, co powoduje obrót bryły, jej przybliżenie bądź oddalenie oraz zmianę liczby podziałów pionowych lub poziomych.
4. Wynik działania algorytmu:
Rysunek przedstawia 1/4 sfery wykreślonej w trybie GL_TRIANGLE_STRIP przy pomocy zamieszczonego powyżej algorytmu. Została tu zwiększona liczba przedziałów pionowych i poziomych, co pozwoliło na przybliżenie żądanego kształtu.
5. Wnioski:
Przy pomocy bibliotek OpenGL oraz GLUT jesteśmy w stanie wygenerować na ekranie obraz bryły zadanej algorytmem. Przy rysowanie należy jednak zwracać uwagę na dokładne dopasowanie kątów wyrażonych w radianach, ponieważ przy niewielkiej niedokładności, przy iteracjach pętli for, błędy w obliczeniach współrzędnych wierzchołków narastają, co może spowodować niedopasowanie ścian bryły. Wyświetlony w ten sposób obraz ma ściany, które nie zachodzą na siebie, bądź wychodzą poza obszar bryły, tworząc postrzępione powierzchnie. Dzięki zmiennym globalnym możliwe jest łatwe ustawianie parametrów bryły oraz położenia obserwatora. Kąty w pętlach można wyrażać w radianach bądź w stopniach, jednak łatwiejsze w użyciu są radiany, ponieważ nie wymagają dalszej konwersji w funkcjach sin oraz cos. Algorytm generujący bryłę zadaną w wykonywanym przeze mnie programie jest dostosowany do zmiany trybu rysowania z GL_TRIANGLE_STRIP na inny, np. GL_TRIANGLE_FAN bez zmiany właściwości zarysu bryły. Algorytmem tym można również się posłużyć rysując dowolny wycinek sfery bądź całą sferę. Należy wtedy tylko pamiętać o właściwej zmianie zmiennych potrzebnych do wyznaczenia współrzędnych wierzchołków punktów oraz odpowiedniej zmianie wartości zmiennych pętli for zarówno wewnętrznej, jak i zewnętrznej.
6. Kod całego programu „sześcian.c”
//////////////////////////////////////////////////////////////////////////////////////////
//
// Program wyswietlajacy szescian w rzucie perspektywicznym. Dlugosc boku szescianu
// moze byc interaktywnie zmieniana za pomoca klwiszy '+' i '-'.
//
//////////////////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
// Definicja stalych
#define DLUGOSC_BOKU 1.0
#define OBSERWATOR_ODLEGLOSC 20.0
#define OBSERWATOR_OBROT_X 20.0
#define OBSERWATOR_OBROT_Y 20.0
#define OBSERWATOR_FOV_Y 30.0
// Zmienne globalne
double r = DLUGOSC_BOKU; // Dlugosc boku szescianu
int szerokoscOkna = 800;
int wysokoscOkna = 600;
double x,y, delta=0.155;
int odl=OBSERWATOR_ODLEGLOSC;
int obr_x=OBSERWATOR_OBROT_X;
int obr_y=OBSERWATOR_OBROT_Y;
// Prototypy funkcji
void RysujSzescian(double a);
void UstawParametryWidoku(int szer, int wys);
void WyswietlObraz(void);
void ObslugaKlawiatury(unsigned char klawisz, int x, int y);
//////////////////////////////////////////////////////////////////////////////////////////
// Funkcja rysujaca 1/4 sfery w trybie GL_TRIANGLE_STRIP o promieniu 1 i srodku w punkcie (0, 0, 0)
void RysujSzescian(double r)
{
for(y=-1.57;y<1.57;y+=delta)
{
for(x=1.57-delta;x<=3.14;x+=delta)
{
glBegin(GL_TRIANGLE_STRIP);
glVertex3f(r*cos(y)*cos(x) , r*sin(y) , r*cos(y)*sin(x));
glVertex3f(r*cos(y)*cos(x+delta) , r*sin(y) , r*cos(y)*sin(x+delta));
glVertex3f(r*cos(y+delta)*cos(x+delta) , r*sin(y+delta) , r*cos(y+delta)*sin(x+delta));
glVertex3f(r*cos(y+delta)*cos(x) , r*sin(y+delta) , r*cos(y+delta)*sin(x));
glEnd();
}
}
for(y=-1.57;y<1.57;y+=delta)
{
for(x=3,14;x<4.71;x+=delta)
{
glBegin(GL_TRIANGLE_STRIP);
glVertex3f(0 , r*sin(y) , -r*cos(y)*sin(x));
glVertex3f(0 , r*sin(y) , -r*cos(y)*sin(x+delta));
glVertex3f(0 , r*sin(y+delta) , -r*cos(y+delta)*sin(x+delta));
glVertex3f(0 , r*sin(y+delta) , -r*cos(y+delta)*sin(x));
glEnd();
}
}
for(y=-1.57;y<1.57;y+=delta)
{
for(x=3,14;x<4.71;x+=delta)
{
glBegin(GL_TRIANGLE_STRIP);
glVertex3f(r*cos(y)*cos(x) , r*sin(y) , 0);
glVertex3f(r*cos(y)*cos(x+delta) , r*sin(y) , 0);
glVertex3f(r*cos(y+delta)*cos(x+delta) , r*sin(y+delta) , 0);
glVertex3f(r*cos(y+delta)*cos(x) , r*sin(y+delta) , 0);
glEnd();
}
}
}
//////////////////////////////////////////////////////////////////////////////////////////
// Funkcja ustawiajaca parametry rzutu perspektywicznego i rozmiary viewportu. Powinna
// być wywolywana kazdorazowo po zmianie rozmiarow okna programu.
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);
}
//////////////////////////////////////////////////////////////////////////////////////////
// Funkcja wyswietlajaca pojedyncza klatke animacji
void WyswietlObraz(void)
{
// Wyczyszczenie bufora koloru i bufora glebokosci
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
// Przejscie w tryb modyfikacji macierzy przeksztalcen geometrycznych
glMatrixMode(GL_MODELVIEW);
// Zastapienie aktywnej macierzy macierza jednostkowa
glLoadIdentity();
// Ustalenie polozenia obserwatora
glTranslatef(0, 0, -odl);
glRotatef(obr_x, 1, 0, 0);
glRotatef(obr_y, 0, 1, 0);
// Narysowanie szescianu
RysujSzescian(r);
// Przelaczenie buforow ramki
glutSwapBuffers();
}
//////////////////////////////////////////////////////////////////////////////////////////
// Funkcja obslugi klawiatury
void ObslugaKlawiatury(unsigned char klawisz, int x, int y)
{
if(klawisz == '+')
r *= 2.0;
else if (klawisz == '-')
r /= 2.0;
else if (klawisz == 'p')
{
if (delta<0.5) delta=delta+0.01;
else if (delta =0.5) delta=0.2;
}
else if (klawisz == 'o')
{
if (delta>0.155) delta=delta-0.01;
else if (delta =0.155) delta=0.5;
}
else if (klawisz == 's') odl=odl+2;
else if (klawisz == 'w') odl=odl-2;
else if (klawisz == 'a') obr_x=obr_x+2;
else if (klawisz == 'd') obr_x=obr_x-2;
else if (klawisz == 'q') obr_y=obr_y+2;
else if (klawisz == 'e') obr_y=obr_y-2;
else if (klawisz == 27)
exit(0);
}
//////////////////////////////////////////////////////////////////////////////////////////
// Glowna funkcja programu
int main(int argc, char **argv)
{
// Zainicjowanie biblioteki GLUT
glutInit(&argc, argv);
// Ustawienie trybu wyswietlania
glutInitDisplayMode (GLUT_DOUBLE|GLUT_RGB|GLUT_DEPTH);
// Ustawienie polozenia dolenego lewego rogu okna
glutInitWindowPosition(100, 100);
// Ustawienie rozmiarow okna
glutInitWindowSize(szerokoscOkna, wysokoscOkna);
// Utworzenie okna
glutCreateWindow("Szescian");
// Odblokowanie bufora glebokosci
glEnable(GL_DEPTH_TEST);
// Ustawienie wartosci czyszczacej zawartosc bufora glebokosci
glClearDepth(1000.0);
// Ustawienie koloru czyszczenia bufora ramki
glClearColor (0.0f, 0.0f, 0.3f, 0.0f);
// Wlaczenie wyswietlania wielokatow w postaci obrysow (przydatne w celach diagnostycznych).
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
// Zarejestrowanie funkcji (callback) odpowiedzialnej za
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;
}