Opracował: mgr inż. Paweł Macioł
Laboratorium Grafiki Komputerowej
Ćwiczenie 3 - „Perspektywa. Sterowanie położeniem obserwatora”
W tym ćwiczeniu zapoznamy się ze sposobami perspektywicznej projekcji 3D. Zostanie pokazany sposób prostej interakcji użytkownika mającej wpływ na położenie obserwatora oraz oglądanych obiektów.
Przestrzeń obiektu (inaczej przestrzeń modelu) - jest to układ współrzędnych obiektu, w którym ten obiekt jest definiowany. Przykładowo: dla sfery jej przestrzenią obiektu może być układ współrzędnych, którego środek jest środkiem sfery; dla walca środek układu może leżeć w środku podstawy, a kierunek osi z jest zgodny z kierunkiem osi symetrii tego walca. Położenie wierzchołków w przestrzeni obiektów jest określone jako wektor [x, y, z].
Współrzędne homogeniczne - to czteroelementowy wektor postaci [x, y, z, w], w którym pierwsze 3 składniki określają położenie w przestrzeni 3D, a czwarty to dodatkowy parametr w. Jeśli wektor położenia zostaje wyrażony za pomocą wektora 3-elementowego to zakłada się, że w = 1. Matematyczne znaczenie parametru w jest takie, że jest to wartość przez którą należy podzielić x, y i z by uzyskać położenie w postaci tradycyjnej. Tak więc jest to pewien współczynnik skali:
Przestrzeń świata - przestrzeń konkretnego obiektu nie ma związku z innymi obiektami posiadającymi własne przestrzenie. Przestrzeń świata definiuje pewien bezwzględny punkt odniesienia dla wszystkich obiektów sceny. Jeśli przyjmiemy że przestrzenią świata jest sala laboratoryjna, to obiekty w niej występujące (komputery, stoły, krzesła) mają różne położenie i ukierunkowanie.
Przekształcenie modelu - opisuje sposób w jaki obiekt w przestrzeni modelu jest transformowany do przestrzeni świata. Na przykład, musimy obrócić, przeskalować i przesunąć stolik by znalazł się on w odpowiednim miejsu w laboratorium. Wszystkie te przekształcenia można zapisać matematycznie jako macierze 4x4 i składać je w jedną macierz transformacji, co jest istotne dla wydajności programu. Mnożąc położenie punktu w postaci homogenicznej (w = 1) w przestrzeni modelu przez macierz 4x4 reprezentującą przekształcenie modelu w przestrzeń świata, otrzymamy to samo położenie opisane w przestrzeni świata.
Przestrzeń oka (inaczej przestrzeń widoku) - to przestrzeń obserwatora, czyli układ współrzędnych obserwatora względem, którego opisujemy położenie obserwowanych obiektów w scenie. W standardzie OpenGL przyjęło się, że domyślnie obserwator („oko”) znajduje się w początku swojego układu współrzędnych, a kierunek patrzenia na scenę jest zgodny z kierunkiem osi z i biegnie wzdłuż jej ujemnych wartości.
Przekształcenie widoku - konwertuje położenie w przestrzeni świata na położenie w przestrzeni obserwatora. To przekształcenie da się zestawić w postaci macierzy 4x4 i nazywane jest macierzą model-widok. Jest to macierz, która łaczy w sobie przekształcenie modelu i przekształcenie widoku.
Przestrzeń przycięcia - kiedy położenia znajdują się już w przestrzeni oka, kolejnym krokiem jest określenie, które z nich znajduje się w widocznym obszarze. Zatem kolejnym układem po przestrzeni oka jest przestrzeń przycięcia. Wyjaśnienie tej techniki można prześledzić obserwując działanie funkcji np. glOrtho().
Przekształcenie, które konwertuje współrzędne przestrzeni oka do przestrzeni przycięcia jest nazywane przekształceniem rzutowania. To przekształcenie definiuje obszar przestrzeni oka, w którym znajdują się widoczne obikety. W OpenGL wszystko co ma być widoczne musi się znajdować w sześcianie, którego każdy punkt spełnia nierówności: -w ≤ x ≤ w, -w ≤ y ≤ w, -w ≤ z ≤ w. Przekształcenie to podobnie jak wcześniej wymienione da się zestawić w postaci tzw. macierzy rzutowania o rozmiarach 4x4.
Znormalizowane współrzędne przycięcia - współrzędne przycięcia maja postać homogeniczną [x, y, z, w], lecz koniecznym jest aby uzyskać położenie w przestrzeni dwu-wymiarowej (x,y) wraz zadaną wartością głębi. Uzyskuje się to dzieląc składowe wektora x, y, z przez składnik w. W ten sposób otrzymane współrzędne noszą nazwę znormalizowanych współrzędnych urządzenia. Na ostatnim etapie następuje transformacja znormalizowanych współrzędnych urządzenia na współrzędne okna, które mierzone są w pikselach.
Dodaj dom kodu z ćwiczenia 2 następującej definicji globalnej zmiennej:
/* Pozycja obserwatora */
GLfloat viewer_pos[] = { 0.0, 0.0, 10.0 };
Dokonaj modyfikacji kodu z ćwiczenia 2 tak aby na ekranie uzyskać poniższy rysunek:
W pomocy VS znajdź informacje na temat funkcji glPerspective() Zmodyfikuj ciała funkcji ReshapeWindow() oraz RenderScene() zgodnie z poniższym.
/* Tę funkcję wywołuje system w momencie gdy uytkownik zmieni myszą
* rozmiar głownego okna. jej zadaniem jest zachowanie propocji wymiarów
* rysowanych obiektów niezależnie od wymiarów okna.
*/
void ReshapeWindow(int width, int height)
{
// Ustawiamy układ współrzędnych obserwatora
glMatrixMode(GL_PROJECTION);
// Resetujemy macierz projkecji
glLoadIdentity();
// Ustawiamy perspektywę
gluPerspective(70.0, 1.0, 1.0, 20.0);
// Korekta
if(width <= height)
glViewport(0, (height - width)/2, width, width);
else
glViewport((width - height)/2, 0, height, height);
// Ustawiamy macierz modelu
glMatrixMode(GL_MODELVIEW);
// Resetujemy macierz modelu
glLoadIdentity();
}
/* W tej funkcji określamy to co ma byc narysowane na ekranie.
* Jest wywoływana zawsze wtedy, gdy trzeba przerysować ekran - bufor ramki.
*/
void DrawScene(void)
{
// Czyścimy okno aktualnym (domyślnym) kolorem oraz resetujemy bufor głębi
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Resetujemy bieżącą macierz
glLoadIdentity();
// Definiujemy położenie obserwatora
gluLookAt(viewer_pos[0],viewer_pos[1],viewer_pos[2],
0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
// Rysujemy osie układu
DrawSceneAxes();
// Zamieniamy bufory ramki
glutSwapBuffers();
}
W powyższym przypadku obserwator znajduje się w punkcie (0,0,10). Zmień tę pozycję na (4, -4, 10). Ustaw współrzędne wektora zenitu na [1, 1, 0]. Jak zmiany te wpłyną na rzutowanie?
Interakcja z obiektem w scenie poprzez użycie myszy
Kolejnym zadaniem jest wzbogacenie naszego programu o funkcjonalność, która będzie polegała na tym, że przy wciśniętym lewym klawiszu myszy i jednoczesnym jej ruchu w lewo bądź w prawo, czajnik wykona stosowny obrót w danym kierunku względem osi y.
W sekcji przeznaczonej na definicję zmiennych globalnych dodaj następujące definicje:
/* Kat obrotu czajnika */
GLfloat theta = 0.0f;
/* Przelicznik pixeli na kąt */
GLfloat pixels2angle = 0.0;
/* Status lewego przycisku myszy:
0- zwolniony
1- wciśnięty */
GLint lbutton_status = 0;
/* Ostatnia pozycja kursora myszy */
GLint x_last_pos = 0;
/* Przemieszczenie kursora */
GLint x_delta = 0;
W funkcji main() dodaj rejestracje funkcji:
// Rejestracja funkcji odpowiedzialnej stan myszy
glutMouseFunc(MouseFunc);
// Rejestracja funkcji odpowiedzialnej za ruch myszy
glutMotionFunc(MouseMotion);
Zaimplementuj zarejestrowane funkcje:
/* Funkcja obsługująca mysz - bada stan klawiszy
* i ustawia odpowiednie zmienne
*/
void MouseFunc(int button, int state, int x, int y)
{
if(button == GLUT_LEFT_BUTTON && state == GLUT_DOWN)
{
// Zapamętujemy obecne położenie myszy
x_last_pos = x;
// Przycisk lewy wcisnięty
lbutton_status = 1;
} else {
// Przycisk jest zwolniony
lbutton_status = 0;
}
}
/* Funkcja monitoruje położenie myszy i ustala odpowiednie zmienne */
void MouseMotion(GLsizei x, GLsizei y)
{
// Wyliczamy aktualne przemieszczenie
x_delta = x - x_last_pos;
// Zapamiętujemy aktualne położenie
x_last_pos = x;
// Odświeżamy okno
glutPostRedisplay();
}
Tuż przed rysowaniem dzbanka dodaj:
// Jeśli wciśnięto lewy klawisz myszy
if(lbutton_status = 1)
{
// Zwiększ kąt
theta += x_delta*pixels2angle;
}
// Obrót w okół osi y
glRotatef(theta,0.0f, 1.0f, 0.0f);
W na początku funkcji ReshapeWindow() dodaj przeliczenie pikseli na stopnie:
// Przeliczamy piksele na stopnie
pixels2angle = 360.0f/(float)width;
Skopiuj i uruchom program.
Zmodyfikuj program dodając obrót w około osi x.
Dodaj funkcjonalność prawego klawisza myszy, którego wciśnięcie i ruch w kierunku pionowym powoduje zbliżanie/oddalanie się od obiektu.
Odwróćmy problem. Teraz niech dzbanek będzie nieruchomy i będzie się rysował w początku układu współrzędnych. Położenie obserwatora względem obiektu (względem początku układu współrzędnych) można zapisać we współrzędnych biegunowych:
,
gdzie: θ,φ- to odpowiednie kąty przy wektorze wodzącym o długości R