Temat: Transformacje 3D 1 Wstęp teoretyczny

Transkrypt

Temat: Transformacje 3D 1 Wstęp teoretyczny
Grafika komputerowa 3D
Instrukcja
laboratoryjna
Temat: Transformacje 3D
11
Przygotował: dr inż. Grzegorz Łukawski, mgr inż. Maciej Lasota, mgr inż. Tomasz
Michno
1 Wstęp teoretyczny
Bardzo często programując grafikę 3D występuje potrzeba dokonywania przekształceń
obiektów (przesuwanie, obracanie, skalowanie itp.). W bibliotece OpenGL, w celu dostarczenia
tych funkcjonalności, zastosowano technikę polegającą na modyfikowaniu całego układu
współrzędnych, dzięki czemu przekształcenia nakładają się na siebie. Od strony programisty
operacja ta polega na modyfikowaniu macierzy modelowania, zazwyczaj za pomocą dostarczanych
funkcji, opisanych w dalszej części instrukcji.
1.1 Przekształcenia
1.1.1
Translacja
Translacja jest przekształceniem polegającym na przesunięciu obiektu o podany wektor
(mówiąc ściślej przesunięciu całego układu współrzędnych). W bibliotece OpenGL realizowane jest
to za pomocą funkcji:
void glTranslatef(GLfloat x, GLfloat y, GLfloat z)
void glTranslated(GLdouble x, GLdouble y, GLdouble z)
Obie funkcje przyjmują 3 parametry: x, y oraz z, które informują bibliotekę o ile jednostek
przesunąć obiekt na osiach OX, OY oraz OZ. Funkcja mnoży bieżąca macierz modelowania
(widoku modelu) przez macierz translacji. Następnie nowa macierz staje się bieżącą macierzą
modelowania.
Przykład 1.
Przesunięcie trójkąta o wektor [-250, -100, -100]:
void display() {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
glColor3f(0.0, 0.0, 1.0);
1/7
glTranslatef(-250, -100, -100); /* <===== translacja, każdy obiekt po tym wywołaniu
będzie przesunięty o ten wektor [-250, -100, -100] */
glBegin(GL_TRIANGLES);
glVertex3f(0, 200, 0);
glColor3f(0.0, 1.0, 1.0);
glVertex3f(50, 220, 0);
glColor3f(1.0, 1.0, 1.0);
glVertex3f(100, 200, 0);
glEnd();
glutSwapBuffers();
}
1.1.2
Skalowanie
Skalowanie, podobnie jak w bibliotece Allegro, polega na zmniejszeniu lub powiększeniu
obiektu o zadany współczynnik, zdefiniowany dla każdej z osi:
void glScalef(GLfloat x, GLfloat y, GLfloat z)
void glScaled(GLdouble x, GLdouble y, GLdouble z)
Funkcje przyjmują współczynniki skalowania dla kolejnych osi. Wartość współczynnika równa 1
oznacza pozostawienie figury bez zmian.
Przykład 2.
Zmniejszenie obiektu w osi OX o połowę:
glScalef(0.5, 1, 1);
// zwykłe rysowanie trójkątów
glBegin(GL_TRIANGLES);
glVertex3f(0, 200, 0); // pierwszy punkt
glColor3f(0.0, 1.0, 1.0);
glVertex3f(50, 220, 0); // drugi punkt
2/7
glColor3f(1.0, 1.0, 1.0);
glVertex3f(100, 200, 0); // trzeci punkt
glEnd();
1.1.3
Rotacja
Rotacja w OpenGL polega na obrocie układu współrzędnych wokół podanego wektora (np.
wektor [0, 1, 0] obróci nam wokół osi OY):
void glRotatef(GLfloat angle, GLfloat x, GLfloat y, GLfloat z)
void glRotated(GLdouble angle, GLdouble x, GLdouble y, GLdouble z)
Parametr angle określa o jaki kąt (w stopniach) będzie dokonywany obrót, natomiast x, y i z są to
poszczególne składowe wektora. Funkcja mnoży bieżącą macierz modelowania (widoku modelu)
przez macierz obrotu. Kąt obrotu określany jest w kierunku przeciwnym do ruchu wskazówek
zegara, sam obrót dokonywany jest wokół osi wyznaczonej przez wektor o początku w układzie
współrzędnych i końcu w punkcie (x,y,z). Nowa macierz staje się bieżącą macierzą modelowania.
1.1.4
Własne mnożenie macierzy
Istnieje
również
możliwość
własnego,
ręcznego
wykonania
wymienionych
(i niewymienionych) przekształceń, za pomocą mnożenia bieżącej macierzy przez drugą macierz.
Odpowiedzialna za to jest funkcja:
void glMultMatrixd(const GLdouble *
m);
void glMultMatrixf(const GLfloat *
m);
Więcej informacji można znaleźć pod poniższym adresem:
http://www.opengl.org/sdk/docs/man/xhtml/glMultMatrix.xml
1.2 Inne przydatne funkcje
Poniżej
zostały
zebrane
funkcje,
które
czasami
są
przydatne
w aplikacjach
z przekształceniami obiektów.
1.2.1
Resetowanie macierzy
Na początku funkcji rysującej należy zawsze wczytać początkowe wartości do macierzy
3/7
modelowania. Dzięki temu unikniemy sytuacji, w której przekształcenia będą się nakładały na
siebie „w nieskończoność”. Aby „zresetować” macierz widoku należy wywołać funkcję:
void glLoadIdentity(
void);
Funkcja ta ładuje macierz jednostkową, dzięki czemu układ współrzędnych zostaje ustawiony
na domyślnym położeniu.
Oprócz macierzy modelowania, glLoadIdentity() można stosować do innych macierzy (tak
na prawdę glLoadIdentity po prostu wczytuje macierz jednostkową do aktualnie używanej
macierzy). Przełączanie pomiędzy macierzami wykonuje się za pomocą glMatrixMode:
glMatrixMode(GL_PROJECTION);
// Macierz projekcji
glMatrixMode(GL_MODELVIEW); // macierz modelowania
1.2.2
Tryb wypełniania
Tryb wypełniania wielokątów umożliwia decydowanie o tym, jak ma być wypełniona
przednia, a jak tylna strona wielokąta. Do ustawia trybu wypełniania wielokątów służy funkcja:
void glPolygonMode(GLenum face, GLenum mode)
Jej pierwszy parametr określa, których ścian będzie dotyczyć operacja (GL_FRONT, GL_BACK,
GL_FRONT_AND_BACK). Drugi parametr określa styl wypełniania ścian wielokąta (GL_LINE,
GL_POINT, GL_FILL). Domyślnie jest ustawiony GL_FILL czyli wypełnianie całego wielokąta.
GL_LINE oznacza że będą widocznie jedynie linie tzw. tryb wireframe (druciak), GL_POINT
oznacza, że będą widoczne jedynie punkty wielokąta.
1.2.3
Widoczność i ukrywanie krawędzi
Często niepotrzebne jest rysowanie wewnętrznych ścian obiektów, rysowanie takie zajmuje
jedynie dodatkową moc obliczeniową komputera. OpenGL umożliwia automatyczne usuwanie
niewidocznych ścian. Aby ukryć niewidoczne ściany należy włączyć opcję ukrywania
niewidocznych
powierzchni
wielokątów
za
pomocą
funkcji
glEnable
z parametrem
GL_CULL_FACE.
Następnie należy wywołać funkcję glCullFace.
void glCullFace(GLenum face)
Funkcja ta przyjmuje jeden z trzech parametrów GL_FRONT (ściana przednia), GL_BACK (ściana
tylna), GL_FRONT_AND_BACK (ściana przednia i tylna).
4/7
Ukrywanie krawędzi wykorzystywane jest w przypadku rysowania obiektów w trybie tzw.
wireframe (siatki). Do ukrywani krawędzi służy specjalna funkcja OpenGL:
void glEdgeFlag(GLenum flag)
Funkcja ta przyjmuje jeden z dwóch parametrów GL_TRUE (rysuj krawędź), GL_FALSE (nie
rysuj krawędzi).
Przykład 3:
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glBegin(GL_POLYGON);
glColor3f(0.5f, 0.5f, 0.5f);
glEdgeFlag(GL_FALSE);
glVertex3f(0.0f, 2.0f, 0.0f);
glEdgeFlag(GL_TRUE);
glVertex3f(2.0f, 2.0f, 0.0f);
glVertex3f(2.0f, -2.0f, 0.0f);
glVertex3f(-4.0f, -2.0f, 0.0f);
glVertex3f(-5.0f, 0.0f, 0.0f);
glEnd();
1.2.4
Timer
Czasami występuje potrzeba wykonywania kodu w pewnych, ustalonych odstępach czasu
(np. animacja). W celu rozwiązania tego problemu programista/programiści biblioteki glut dodali
do niej obsługę timerów. Timer jest funkcją, która wykonuje się co pewien zadany czas.
Glut wymaga, aby funkcja ta miała ustalone parametry:
void funkcjaTimera(int value);
Następnie należy zarejestrować ją (podobnie jak funkcje rysowania i klawiatury) za pomocą
następującej funkcji:
void glutTimerFunc(unsigned int msecs, void (*func)(int value), value);
Parametry są następujące:
5/7
msecs – czas w milisekundach, po którym zostanie wywołana funkcja timera
func – wskaźnik na funkcję timera
value – wartość przekazywana do funkcji timera (parametr value funkcji timera będzie ustawiany
na tą wartość)
Uwaga!
Ustawienie timera w funkcji glutTimerFunc spowoduje tylko jednorazowe jego wywołanie. Dlatego
w funkcji timera najlepiej jest użyć jej ponownie (patrz przykład 4).
Glut umożliwia zadeklarowanie dowolnej liczby timerów (np. można zadeklarować dwa timery,
które wykonują się o różnym czasie).
Przykład 4:
void funkcjaTimera(int val) {
// kod wykonywany w timerze
//...
// odrysowanie ekranu po wprowadzeniu zmian (jeśli jest taka potrzeba):
glutPostRedisplay();
// ponowne ustawienie timera (co 1000 ms = 1s):
glutTimerFunc(1000, funkcjaTimera, 0);
}
int main(int argc, char *argv[]) {
glutInit(&argc, argv);
/*
...
*/
// ustawienie timera (wywoła funkcję funkcjaTimera po 1000 ms = 1s):
glutTimerFunc(1000, funkcjaTimera, 0);
glutMainLoop();
6/7
return(0);
}
2 Zadania
Napisz program, który będzie wykonywał animacje (z użyciem timera) kilku obiektów
(dowolnych) z użyciem przedstawionych w instrukcji transformacji 3D (translacja, obrót,
skalowanie), np. odbijające się od ścian okna obiekty dostarczane przez gluta. Aplikacja powinna
używając timera zmieniać kolory obiektów (co 5 sekund) oraz zmieniać tryb wypełniania (co 8
sekund) – można użyć dwóch timerów.
Wskazówki i uwagi
•
nie używaj funkcji gluLookAt(); ponieważ wprowadza przekształcenia sceny
•
na końcu funkcji display() umieść wywołanie funkcji glutPostRedisplay(); która odświeża
zawartość ekranu
•
do poprawnego wyświetlania obiektów należy prawidłowo obsłużyć bufor głębokości
1. Inicjalizacja trybu wyświetlania – dodanie parametru GLUT_DEPTH
2. Włączenie testu głębokości – parametr GL_DEPTH_TEST
3. Czyszczenie bufora głębokości przed rysowaniem – param. GL_DEPTH_BUFFER_BIT
7/7