WOJSKOWA AKADEMIA TECHNICZNA
Laboratorium z przedmiotu
Grafika komputerowa
SPRAWOZDANIE
Laboratorium nr 2
Wykonanie: Justyna Kozon
Grupa: I9X5S1
Data wykonania ćwiczenia:25.11.2010
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 [7,14].
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, znacznie ułatwiających implementację i określenie położenia w przestrzeni kolejnych punktów. Sposób ich zamiany przedstawia rysunek:
Widać zatem, że do określenia punktu w przestrzeni potrzebny jest nam jedynie kąt odchylenia od osi X oraz promień łączący środek układu z określanym przez nas punktem. Podstawa stożka będzie rysowana w płaszczyźnie OXOZ. Ze względu na wykorzystanie funkcji sinus i cosinus należy dodać do plików nagłówkowych <math.h>, dzięki czemu będzie można bez przeszkód używać ich przy zapisywaniu współrzędnych punktów.
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ł. Zastosowanie tego twierdzenia przedstawia rysunek:
Z równania zawartego na rysunku można łatwo wyznaczyć r i używać tej wartości przy określaniu współrzędnych.
Sposoby rozwiązania zadania i kod źródłowy.
Na samym początku należało dołączyć plik nagłówkowy za pomocą instrukcji:
#include <math.h>
Od tej pory możemy już używać funkcji sinus i cosinus. Następnie do zdefiniowanych stałych dołączyłam dodatkowo definicję liczby PI, gdyż będziemy z niej później często korzystać.
#define PI 3.1415
Kolejną rzeczą do zrobienia było określenie dodatkowych stałych występujących w programie. Powinny znaleźć się tu stałe, które będą zmieniane z poziomu klawiatury. W ten sposób będzie można używać ich w funkcji rysującej obiekt oraz zmieniać je za pomocą funkcji obsługi klawiatury. Określone przeze mnie stałe:
float pion=7.0; //określa liczbę podziałów pionowych stożka
float poziom=7.0; //określa liczbę podziałów poziomych stożka
float ile=7.0; //określa ile wycinków poziomych ma zostać zbudowane
float ile2=7.0; //określa ile wycinków pionowych ma zostać zbudowane
Dodatkowo, ponieważ nie można zmieniać w programie stałych zdefiniowanych na początku dołączyłam dwie zmienne określające obrót dookoła osi X oraz Y, dzięki czemu łatwiej było sprawdzić poprawność wykonanej figury mając możliwość zmiany kąta obrotu figury (nie posiadałam tej funkcjonalności podczas sprawdzania zadania na laboratorium przez co sprawdzanie to było bardziej kłopotliwe). Funkcja ta została dodatkowo dołączona to funkcji obsługi klawiatury.
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);
Teraz można przystąpić do analizy funkcji rysującej stożek. Na początku każdej funkcji określamy zmienne w niej występujące:
float H=5.0; //wysokość stożka
float kat,x,y,z,temp,r;
float R=3.0; //promień podstawy
Następnie, ponieważ ilość podziałów jest zmienna będzie należało za każdym wyrysowaniem figury określać kąt o jaki zmieniać się będzie współrzędna przebiegająca wzdłuż osi OXZ. Do narysowania jest 3/8 stożka zatem w sumie kąt ten będzie się zmieniał maksymalnie od 0 do 0.75π, gdyż całe koło zmienia się od 0 do 2π.
Konwencja przyjęta w OpenGL różni się od tej, którą znamy z przedmiotów matematycznych i kąty liczymy zgodnie z ruchem wskazówek zegara.
Kąt co jaki będzie rysowany kolejny punkt wyznaczamy za pomocą następującej instrukcji:
float krokalfa=0.75*PI/pion;
Czyli maksymalny kąt dzielimy przez liczbę podziałów pionowych i otrzymujemy odpowiedni skok kąta. Podobnie postępujemy w wyznaczaniu kroku związanego z podziałami poziomymi. Wysokość stożka dzielimy przez liczbę podziałów poziomych, przez co otrzymujemy odpowiedni skok wysokości.
float kroky=H/poziom;
W związku z tym , że w programie mamy możliwość niezależnej zmiany liczby podziałów poziomych, pionowych oraz ilości wyświetlonych rzędów poziomych oraz pasków pionowych może nastąpić sytuacja, w której np. zwiększyliśmy ilość podziałów pionowych do 12 i narysowaliśmy tyle właśnie rzędów pionowych i zmniejszamy ilość pionowych podziałów, a zostawiamy liczbę tych wyświetlanych. Wtedy pojawia się niepożądane zjawisko związane z dorysowywaniem dodatkowych pasków pionowych wykraczających poza kąt 0.75π, który powinien być maksymalny. Związane to jest ze zwiększeniem kroku kątowego oraz tym, że ograniczenia pętli są związane z tym właśnie krokiem i ilością pasków do wyświetlenia. Analogiczna sytuacja występuje w przypadku rzędów poziomych. Aby uchronić się przed tego typu sytuacjami dodałam 2 instrukcje if();
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)
{
temp=kat;
y=0;
glVertex3f(R*cos(temp),y, R*sin(temp));
glVertex3f(0,0,0);
temp+=krokalfa;
glVertex3f(R*cos(temp),y, R*sin(temp));
}
glEnd();
Podstawa jest budowana w trybie GL_TRIANGLES. Znajduje się ona w płaszczyźnie OXZ zatem współrzędna y zawsze będzie równa 0. Użyłam pętli for, 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. Jeśli wartość ile2=pion, czyli ilości podziałów pionowych to zostanie zbudowana cała podstawa. Po każdym przejściu pętli kąt zostaje zwiększany o odpowiednią wartość dopóki spełnia warunek pętli. Wewnątrz for() mamy zmienną temp, która przechowuje aktualną wartość kąta. Używamy tu takiej zmiennej zamiast wprost wartości kat, ponieważ będziemy ją zmieniać a nie chcemy wpłynąć na przebieg pętli for, w której kat jest głównym parametrem. Następnie 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 krok alfa 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.
Podstawa z najmniejszą i największą ilością podziałów pionowych oraz 1 rzędem poziomym(widok z góry).
Budowa ściany bocznej stożka:
glBegin(GL_TRIANGLES);
for(kat=0; kat<ile2*krokalfa ; kat+=krokalfa)
{
for(y=0;y<ile*kroky;y+=kroky)
{
r=R*(H-y)/H;
temp=kat;
glVertex3f(r*cos(temp),y,r*sin(temp));
temp+=krokalfa;
glVertex3f(r*cos(temp),y,r*sin(temp));
temp-=krokalfa;
r=R*(H-y-kroky)/H;
glVertex3f(r*cos(temp),y+kroky,r*sin(temp));
}
}
glEnd();
Ściana boczna będzie budowana zarówno „po kole” czyli współrzędne będą obliczane na podstawie wartości biegunowych, jak i w górę, wzdłuż osi Y. Dlatego do zbudowania ściany bocznej potrzebne SA nam 2 pętle for();. Jedna będzie przechodziła wzdłuż osi OXZ, a druga wzdłuż osi OY. Pierwsza pętla for wygląda tak jak w przypadku podstawy i działa w taki sam sposób. Druga z pętli przechodzi od y=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. Zmienna temp pełni taką samą funkcję jak w przypadku podstawy. 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 związany z kątem wynikającym z pętli for, ale na wysokości wyższej o kroky. Punkty te są łączone i następuje odpowiednie zwiększenie kąta w pętli.
Podstawa i 1 rząd poziomy:
Cały stożek, maksymalna liczba podziałów:
Budowa ścian wycięcia:
glBegin(GL_TRIANGLES);
for(y=0;y<ile*kroky;y+=kroky)
{
r=R*(H-y)/H;
glVertex3f(0,y,0);
glVertex3f(r*cos(0),y,0);
glVertex3f(0,y+kroky,0);
glVertex3f(0,y,0);
glVertex3f(r*cos(0.75001*PI),y,r*sin(0.75001*PI));
r=R*(H-y-kroky)/H;
glVertex3f(r*cos(0.75001*PI),y+kroky,r*sin(0.75001*PI));
}
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ę y, 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ą y i ewentualnie modyfikując promień r.
Ściany wycięcia z 1 paskiem pionowym:
Gotowy stożek z różnymi wartościami ilości rzędów poziomych i pionowych pasków oraz różnymi ilościami podziałów:
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 =14 nie wykona się więcej
if(poziom<14)
poziom++;}
else if(klawisz=='a'){ //jeśli poziom=7 nie wykona się
if(poziom>7)
poziom--;}
else if(klawisz=='w'){ //jeśli pion=14 nie wykona się więcej
if(pion<14)
pion++;}
else if(klawisz=='s'){ //jeśli pion=7 nie wykona się
if(pion>7)
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łam funkcji sterującej obrotem stożka oraz miałam niedopracowane dokładnie granice w pętlach for przez co z niektórych przypadkach rysowany był o jeden rząd w pionie lub poziomie za dużo. Granice te zostały przeze mnie poprawione. Dopisałam także warunki umożliwiające wybór ilości pionowych pasków rysowanych przez program. Celem tego ćwiczenia było zapoznanie się z zasadami budowania obiektów w OpenGL za pomocą prymitywów. Dzięki temu łatwiej nam zrozumieć dostępne gotowe funkcje. Wykonując to ćwiczenie można się przekonać, że bez odpowiedniej wprawy w pisaniu tego typu funkcji nie jest to łatwe zadanie. 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.