WOJSKOWA AKADEMIA TECHNICZNA
Laboratorium NR 2 z przedmiotu:
Grafika komputerowa
Autor:
Michał Popławski
I9X6S1
Prowadzący:
mgr inż. Wojciech Sulej
Treść wykonywanych zadań.
Zestaw 4.
1. Narysować 3/8 stożka w trybie GL_TRIANGLES o promieniu podstawy 3 i jej środku w punkcie (0,0,0), wysokości 5.
2. Dodać możliwość zmiany za pomocą klawiatury podziałów poziomych i pionowych figury w zakresie [9,33].
3. Dodać możliwość sterowaniem konstruowaniem figury (budowanie poszczególnych części figury za pomocą klawiszy klawiatury).
Wstęp teoretyczny.
Do wykonania zadania laboratoryjnego należało użyć odpowiednich funkcji związanych z modelowaniem przestrzeni zawartych w bibliotekach OpenGL oraz GLUT.
Podstawowe funkcje użyte do tworzenia figury:
glVertex3f(x,y,z); - za pomocą tej funkcji definiujemy współrzędne punktu, który znajdzie się z określanej przez nas przestrzeni trójwymiarowej (pojedynczy wierzchołek).
glBegin (GLenum mode); - funkcja ta rozpoczyna definicję obiektu. Jako parametr przyjmuje jeden z trybów, w którym będą łączone wierzchołki, co równoznaczne jest z tworzeniem figur. W mojej wersji zadania posługuję się jedynie trybem GL_TRIANGLES.
glEnd (GLenum mode); - funkcja ta kończy definicję obiektu. Występuje po glBegin(); i posiada taki sam argument jak ona.
Tryb GL_TRIANGLES – pracując w tym trybie musimy zdefiniować 3 punkty dla każdego tworzonego trójkąta. Trójkąty nie są ze sobą domyślnie w żaden sposób połączone.
Pozostałe funkcje pochodzą z bibliotek języka C++.
W zadaniu należało stworzyć stożek, którego podstawą jest koło, zatem pomocne w rozwiązaniu jest skorzystanie ze współrzędnych biegunowych.
Ze względu na wykorzystanie funkcji sinus i cosinus należy dodać do plików nagłówkowych <math.h>.
Do budowania ściany bocznej stożka będzie należało wykorzystać twierdzenie Talesa dotyczące stosunku boków trójkąta ze względu na to, że będziemy rysować punkty na różnych wysokościach(względem osi OY), zatem promień w miarę zbliżania się do wierzchołka stożka będzie się zmniejszał.
Analiza kodu.
Na początku dodałem do wartości definiowanych liczbę PI:
#define PI 3.1415
Aby można było zmieniać ilość podziałów pionowych i poziomych stożka, budować figurę krok po kroku oraz wykonywać obroty wokół osi OX i OY należy zdefiniować odpowiednie zmienne globalne:
float pion = 9.0; //określa liczbę podziałów pionowych stożka
float poziom = 9.0; //określa liczbę podziałów poziomych stożka
float ile = 9.0; //określa ile wycinków poziomych ma zostać zbudowane
float ile2 = 9.0; //określa ile wycinków pionowych ma zostać zbudowane
float os_Y=OBSERWATOR_OBROT_Y;
float os_X=OBSERWATOR_OBROT_X;
Ponadto należało w funkcji wyświetlania obrazu zmienić ustalenie położenia obserwatora w taki sposób aby nie zależało ono od określonych wyżej zmiennych zamiast od stałych zdefiniowanych.
glRotatef(os_X, 1, 0, 0);
glRotatef(os_Y, 0, 1, 0);
Tworzenie figury:
Zmienne lokalne w funkcji rysowania figury:
float H = 5.0; //wysokość stożka
float kat = 0.0; //wartość kąta
float wysokosc = 0.0; //na jakim poziomie stożka jesteśmy
float R = 3.0; //promień podstawy
float r = R*(H-wysokosc)/H; //zmniejszający się promień w zależności od wysokości
float krokalfa = (3*PI/4)/pion; //wzrost kata w zależności od ilości podziałów //i obszaru jaki ma zarysować kąt (3/8 * 2*PI = 3*PI/4)
float krokwysokosc = H/poziom; //wzrost wysokości w zależności od ilości podziałów
Aby uchronić się przed rysowaniem poza zakresem 3*PI/4 należy dodać dwie instrukcje:
if(ile>poziom) ile = poziom;
if(ile2>pion) ile2 = pion;
Dzięki pierwszej z nich, jeśli ilość rzędów do wyświetlenia jest większa od ilości poziomów, ilości rzędów przypisywana jest wartość ilości poziomów, dzięki czemu nie zostanie zbudowana dodatkowa, zbędna cześć figury. Podobnie działa 2 instrukcja, która związana jest z ilością pasków pionowych.
Budowa podstawy dolnej stożka:
glBegin(GL_TRIANGLES);
for(kat=0; kat<ile2*krokalfa; kat+=krokalfa)
{
glVertex3f(R*cos(kat), 0.0, R*sin(kat));
glVertex3f(0.0, 0.0, 0.0);
glVertex3f(R*cos(kat+krokalfa), 0.0, R*sin(kat+krokalfa));
}
glEnd();
Podstawa znajduje się na wysokości równej 0. Użyłem pętli, w której kąt zwiększa się od 0 do wartości równej iloczynowi wyliczonego kroku kąta i ilości pasków pionowych do zbudowania. Wewnątrz for() wyznaczamy wartości współrzędnych x oraz z zgodnie ze wzorami na współrzędne biegunowe. Rysujemy pierwszy z punktów o współrzędnych x,y,z, kolejny o współrzędnych (0,0,0), zwiększamy kąt o krokalfa i rysujemy ostatni z punktów. Z tych 3 punktów powstaje 1 trójkąt podstawy. Z kolejnymi przejściami pętli for będą się tworzyć kolejne aż do uzyskania pełnej podstawy lub ewentualnie określonego jej fragmentu.
Efekt graficzny (minimalny i maksymalny podział):
Budowa ściany bocznej stożka:
glBegin(GL_TRIANGLES);
for(kat=0; kat<ile2*krokalfa ; kat+=krokalfa)
{
for(wysokosc=0;wysokosc<ile*kroky;wysokosc+=kroky)
{
r = R*(H-wysokosc)/H;
glVertex3f(r*cos(kat), wysokosc, r*sin(kat));
glVertex3f(r*cos(kar+krokalfa), wysokosc, r*sin(kat+krokalfa));
r = R*(H-wysokosc-krokwysokosc)/H;
glVertex3f(r*cos(kat), wysokosc+krokwysokosc, r*sin(kat));
}
}
glEnd();
Dwie pętle zewnętrzna po kącie, wewnętrzna po wysokości. Pętla wewnętrzna przechodzi od wysokosc=0, czyli od poziomu podstawy, aż do wyliczonej wartości określającej ile chcemy rzędów zbudować. Wewnątrz pętli mamy wyznaczony wzór na promień przekroju poziomego stożka. Punkty rysujemy w następującej kolejności: najpierw punkt, który bezpośrednio wynika z wartości określonych w pętlach, następnie punkt znajdujący się o krokalfa dalej, ale na tej samej wysokości i ostatni z punktów na wysokości wyższej o krokwysokosc.
Cały stożek, maksymalna liczba podziałów:
Budowa ścian wycięcia:
glBegin(GL_TRIANGLES);
for(wysokosc=0;wysokosc<ile*krokwysokosc;wysokosc+=krokwysokosc)
{
r = R*(H-wysokosc)/H;
glVertex3f(0.0, wysokosc, 0.0);
glVertex3f(r*cos(0.0), wysokosc, 0.0);
glVertex3f(0.0, wysokosc+krokwysokosc, 0.0);
glVertex3f(0.0, wysokosc, 0.0);
glVertex3f(r*cos(3*PI/4), wysokosc, r*sin(3*PI/4));
r = R*(H-wysokosc-krokwysokosc)/H;
glVertex3f(r*cos(3*PI/4), wysokosc+krokwysokosc, r*sin(3*PI/4));
}
glEnd();
Jedna ściana wycięcia znajduje się na osi X, zatem dla niej współrzędna z zawsze będzie równa 0. Pętla for określa tylko zmianę wysokosc, ponieważ jest to pojedyncza ściana i nie zmieniamy kątów w pętli, gdyż są one stałe i nie ma takiej potrzeby. Dobieramy odpowiednie współrzędne dla obu ścian wycięcia, zwiększając odpowiednio współrzędną wysokosc oraz modyfikując promień r.
Ściany wycięcia:
Krok po kroku:
Funkcja obsługi klawiatury:
void ObslugaKlawiatury(unsigned char klawisz, int x, int y)
{
if(klawisz == '+')
bok *= 2.0;
else if (klawisz == '-')
bok /= 2.0;
else if(klawisz=='q'){ //jeśli poziom =33 nie wykona się więcej
if(poziom<33)
poziom++;}
else if(klawisz=='a'){ //jeśli poziom=9 nie wykona się
if(poziom>9)
poziom--;}
else if(klawisz=='w'){ //jeśli pion=33 nie wykona się więcej
if(pion<33)
pion++;}
else if(klawisz=='s'){ //jeśli pion=9 nie wykona się
if(pion>9)
pion--;}
else if(klawisz=='e'){ //zwiększa się dopóki nie osiągnie maksymalnej
if(ile<poziom) //wartości, jaką jest liczba podziałów
ile++;}
else if(klawisz=='d'){ //zmniejsza się dopóki jest większe od 0
if(ile>0)
ile--;}
else if(klawisz=='r'){ //zwiększa się dopóki nie osiągnie maksymalnej
if(ile2<pion) //wartości, jaką jest liczba podziałów
ile2++;}
else if(klawisz=='f'){ //zmniejsza się dopóki jest większe od 0
if(ile2>0)
ile2--;}
else if(klawisz=='j') //klawisze do obracania obiektem wzdłuż osi X
os_Y-=1.0; //oraz Y
else if(klawisz=='l')
os_Y+=1.0;
else if(klawisz=='i')
os_X-=1.0;
else if(klawisz=='k')
os_X+=1.0;
else if (klawisz == 27)
exit(0);
}
Wnioski:
Wszystkie zadania postawione przede mną zostały wykonane poprawnie. Na zajęciach laboratoryjnych nie posiadałem budowania krok po kroku figury oraz przy zwiększeniu podziałów rysowały się niepożądane trójkąty, które wyeliminowałem dopierając odpowiednio granice pętli. Aby narysować figurę trzeba dokładnie określać każdy punkt w przestrzeni. Można także zauważyć, że im większa liczba podziałów pionowych oraz poziomych tym dokładniejsze jest odwzorowanie figury, którą tworzymy.