Wizualizacja danych sensorycznych Projekt Tworzenie grafiki stereoskopowej Marek Gulanowski 9 czerwca 2010
Spis treści 1 Wstęp 1 1.1 Koncepcja................................ 1 1.2 Forma realizacji............................. 1 2 Klasa AnaglyphViewer 2 2.1 Generowanie grafiki........................... 2 3 Przykład wykorzystania klasy AnaglyphViewer 2 3.1 Koncepcja programu.......................... 2 3.2 Sposób użycia klasy AnaglyphViewer................. 6 3.3 Interfejs graficzny............................ 6 4 Podsumowanie 8 1 Wstęp 1.1 Koncepcja Tematem projektu było generowanie grafiki stereoskopowej. Jako cel realizacji projektu obrano zbudowanie biblioteki programistycznej, umożliwiające proste generowanie takiej grafiki. Sformułowano następujące postulaty dotyczące tworzonej biblioteki. 1. Wykorzystanie środkowiska Qt4. 2. Generowanie grafiki w środowisku OpenGL. 3. Wykorzystanie biblioteki libqglviewer. Tworzenie grafiki stereoskopowej wymaga dostarczania odmiennych obrazów dla każdego z oczu obserwatora. Najprostszą realizacją takiego założenia jest zastosowanie okularów z filtrami, przepuszczającymi różne barwy składowe, np. czerwoną i niebieską (dla tego typu okularów przeznaczona jest niniejsza implementacja). Obrazy generowane dla poszczególnych oczu mają wtedy różne barwy, ale pomimo tego przez ludzki mózg składane są w jeden obraz i tworzą iluzję trójwymiarowości. Istnieją inne, dużo bardziej zaawansowane technologię generowania obrazu stereoskopowego, jednak każda z nich wymaga albo użycia specjalnego projektora, ekranu, albo okularów aktywnych (przesłaniających na zmianę po jednym oku). Zaletą grafiki anaglifowej jest możliwość korzystania z dowolnego monitora i stosowania bardzo tanich okularów. 1.2 Forma realizacji Podjęto decyzję o stworzeniu klasy AnaglyphViewer, która umożliwiałaby umieszczanie widgetu z grafiką wyświetlaną w formie anaglifu w programach budowanych w środowisku Qt4. Ponadto, w celu weryfikacji działania klasy, postanowione zbudować prosty program w środowisku Qt4, zawierający widget klasy AnaglyphViewer. Założono minimalną funkcjonalność tej aplikacji, gdyż była ona w zamierzeniu jedynie ilustracją użycia stworzonej klasy. 1
2 Klasa AnaglyphViewer Klasa AnaglyphViewer jest klasą pochodną klasy QGLViewer, zdefiniowanej w bibliotece libqglviewer. 2.1 Generowanie grafiki Klasa libqglviewer udostępnia m.in. następujące metody wirtualne. 1. paintgl(), realizująca cały proces generowania grafiki. 2. predraw(), przygotowująca do rysowania figur. 3. draw(), rysująca figury. 4. postdraw(), rysująca dodatkowe elementy (za pomocą QPainter). Działanie metody 1 polega na kolejnym wywołaniu funkcji 2, 3, 4. Aby wykorzystać klasę libqglviewer, należy w programie stworzyć klasę pochodną tej klasy. Warunkiem koniecznym i wystarczającym dla takiej klasy pochodnej jest zdefiniowanie funkcji wirtualnej draw. Jej zadaniem jest narysowanie wizualizowanego obiektu za pomocą funkcji biblioteki OpenGL. Istotny jest tutaj fakt, iż implementując funkcję draw wystarczy wywołać funkcje rysujące, nie należy natomiast realizować funkcji OpenGL niższego poziomu, takich jak np. czyszczenie buforów kolorów czy głębokości wierzchołków. Tym zajmuje się funkcja paintgl, której definiowanie w klasie pochodnej nie jest obowiązkowe. Zgodnie z powyższym, istotą klasy AnaglyphViewer jest reimplementacja funkcji wirtualnej paintgl. Listing?? przedstawia definicję tej funkcji. Ideą działania tej funkcji, kluczowej dla stworzenia efektu stereoskopowego, jest narysowanie dwóch obrazów: dla lewego i prawego oka, do których stosowane są maski barw, pozostawiające jedynie barwę odpowiednio czerwoną i niebieską. Aby obrazy nałożyły się, pomiędzy narysowaniem obrazów należy wyczyścić bufor głębi wierzchołków, nie czyścić natomiast bufora barw. Pozycja oczu wyliczana jest poprzez modyfikację bieżącej pozycję kamery w kierunku wskazywanym przez iloczyn wektorowy wektora określającego kierunek pionowy pola widzenia oraz wektora określającego, jak skierowana jest kamera (w tej kolejności dla lewego oka, w przeciwnej dla oka prawego). Otrzymany wektor jest wektorem znormalizowanym, zostaje zatem przeskalowany z użyciem parametru eyedistance, określającego odległość oczu. Zaletą takiej implementacji efektu stereoskopowego jest fakt, że funkcje predraw() oraz draw() pozostają od niej niezależne i mogą być definiowane w klasach pochodnych (bądź pozostawione w formie zdefiniowanej w klasie QGLVIewer). 3 Przykład wykorzystania klasy AnaglyphViewer 3.1 Koncepcja programu W środowisku Qt4 stworzono program, który może służyć jako przykład zastosowania klasy AnaglyphViewer. Program modeluje zachowanie platformy balansującej. Wykorzystano tu klasy stworzone na potrzeby projektu Roboty mobilne. Wykorzystują one do modelowania dynamiki oraz wykrywania zderzeń bibliotekę Open Dynamics Engine. Model platformy umożliwia bezpośrednie zadawanie sterowania kół platformy, a także przykładanie momentów obrotowych do jej korpusu. Implementuje także 2
Listing 1: Funkcja paintgl() klasy AnaglyphViewer 1 void AnaglyphViewer : : paintgl ( ) 2 { 3 i f ( a n a g l y p h ( ) ) / / wlaczony t r y b 3D Anaglyph 4 { 5 glcolormask ( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE ) ; 6 g l C l e a r (GL_COLOR_BUFFER_BIT GL_DEPTH_BUFFER_BIT ) ; / / w y c z y s z c z e n i e buforow 7 8 glcolormask ( GL_TRUE, GL_FALSE, GL_FALSE, GL_TRUE ) ; / / maska barw d l a lewego oka 9 predraw ( ) ; 10 p o s i t i o n = camera () > p o s i t i o n ( ) 11 + c r o s s ( camera () > upvector ( ), camera () > v i e w D i r e c t i o n ( ) ) 12 e y e D i s t a n c e ( ) / 2 ; 13 camera () > s e t P o s i t i o n ( p o s i t i o n ) ; / / kamera w p o z y c j i lewego oka 14 draw ( ) ; / / n a r y s o w a n i e o b i e k t u 15 16 g l C l e a r ( GL_DEPTH_BUFFER_BIT ) ; / / w y c z y s z c z e n i e b u f o r a g l e b i wierzcholkow 17 / / uwaga : n i e j e s t c z y s z c z o n y b u f o r kolorow (w c e l u n a l o z e n i a s i e obrazow ) 18 19 glcolormask ( GL_FALSE, GL_FALSE, GL_TRUE, GL_TRUE ) ; / / maska barw d l a prawego oka 20 21 g l L o a d I d e n t i t y ( ) ; 22 predraw ( ) ; 23 p o s i t i o n = camera () > p o s i t i o n ( ) 24 + c r o s s ( camera () > v i e w D i r e c t i o n ( ), camera () > upvector ( ) ) 25 e y e D i s t a n c e ( ) / 2 ; 26 camera () > s e t P o s i t i o n ( p o s i t i o n ) ; / / kamera w p o z y c j i prawego oka 27 draw ( ) ; 28 glcolormask (GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE ) ; / / z n i e s i e n i e maski 29 glend ( ) ; / / f u n k c j a standardowo wywolywana na z a k o n c z e n i e f u n k c j i paintgl ( ) 30 } 31 e l s e / / w przypadku wylaczonego t r y b u 3D 32 { 33 QGLViewer : : paintgl ( ) ; / / wywolanie s t a n d a r d o w e j f u n k c j i k l a s y QGLViewer 34 } 35 } 3
regulator PID, który umożliwia przemieszczanie platformy analogicznie do urządzenia typu Segway regulator stabilizujący nachylenie platformy do pionu reaguje na jej wychylanie w określonym kierunku poprzez jazdę w tym kierunku. Pełen diagram klas stworzonego programu prezentuje rysunek 1. 4
5 Rysunek 1: Diagram klas programu wykorzystującego klasę AnaglyphViewer
QGLPlatforma:: init() QGLPlatforma:: animate() T Animacja aktywna? N AnaglyphViewer:: paintgl() Rysunek 2: Diagram przepływu sterowania 3.2 Sposób użycia klasy AnaglyphViewer Jak wspomniano w sekcji 2.1, rolą klasy AnaglyphViewer jest implementacja wirtualnej metody paintgl(), która wywołuje m.in. funkcję draw(), przeznaczoną do rysowania sceny. Zatem aby wykorzystać klasę AnaglyphViewer do rysowania konkretnych obiektów, należy zdefiniować metodę draw() w klasie dziedziczącej klasę AnaglyphViewer. W celu wizualizacji działania symulacji platformy balandującej stworzono klasę QGLPlatforma, która zawiera obiekt klasy PlatformaBalansujaca, realizujący wspomnianą symulację. Jej implementacja metody draw() polega na wywołaniu metody Rysuj() klasy PlatformaBalansujaca, która na podstawie aktualnych danych o pozycji i orientacji platformy w wizualizowanej scenie, wykorzystuje funkcje biblioteki OpenGL do rysowania figur przestrzennych, składających się na platformę. Konieczne jest też zdefiniowanie funkcji wirtualnej init(), która wywoływana jest przez obiekt klasy QGLViewer na początku działania. W tym wypadku służy ona do stworzenia świata zgodnie z API biblioteki Open Dynamics Engine, zdefiniowania obiektów i relacji między nimi. klasa QGLPlatforma implementuje ponadto wirtualną funkcję animate(), w której podejmowany jest krok symulacji i pobierane nowe współrzędne obiektów. Dzięki rozdzieleniu funkcji draw() i animate() a klasie QGLViewer możliwy jest np. ruch kamery w czasie gdy animacja jest wstrzymana. Rysunek 2 ukazuje poglądowy diagram przepływu sterowania w zrealizowanym programie (części związanej z generowaniem grafiki). Na diagramie 3 ukazano natomiast przepływ sterowania wewnątrz funkcji AnaglyphViewer::paintGL(). 3.3 Interfejs graficzny Do budowy interfejsu graficznego aplikacji wykorzystano program Qt Designer. Umieszczenie widgetu AnaglyphViewer w oknie budowanej aplikacji było możliwe dzięki wykorzystaniu funkcji zastępowania obiektów pochodnych Qwidget przez inne tego rodzaju obiekty. 6
QGLViewer:: paintgl N Tryb 3D anaglyph? T Przeniesienie kamery w lewo, zastosowanie maski QGLPlatforma ::draw() Przeniesienie kamery w prawo, zastosowanie maski QGL::Platforma ::draw() Rysunek 3: Diagram przepływu sterowania wewnątrz funkcji AnaglyphViewer::paintGL() 7
Listing 2: Dziedziczenie interfejsu graficznego 1 c l a s s Okno : p u b l i c QMainWindow, p r i v a t e Ui : : MainWindow 2 { 3 Q_OBJECT 4 p u b l i c : 5 Okno ( QWidget r o d z i c = 0 ) ; 6 } ; W celu zintegrowania interfejsu graficznego z aplikacją użyto mechanizmu wielodziedziczenia. Listing 2 przedstawia deklarację klasy Okno, za pomocą której połączono główną część programu z interfejsem graficznym, wygenerowanym za pomocą programu Qt Designer. 4 Podsumowanie Kompilacja i uruchomienie programu zakończyły się sukcesem. Rysunki 4 i 5 przedstawiają działania programu odpowiednio w trybie 3D oraz 2D. A zatem stworzona klasa AnaglyphViewer może być z powodzeniem wykorzystywana w programach tworzonych w środowisku Qt, także z użyciem programu Qt Designer, analogicznie do klasy QGLViewer. Wyniki prac prowokują do zadania pytania: czy tego rodzaju wizualizacja z wykorzystaniem obrazu stereoskopowego typu anaglif może mieć zastosowanie w robotyce. Z pewnością jest to pytanie trudne. Zaletą grafiki stereoskopowej, nawet w tak prostej implementacji jak czerwono-niebieski anaglif, jest możliwość ujrzenia dodatkowego wymiaru, co w przypadku niektórych obrazów może je czynić wyraźniejszymi. Dlatego odpowiedź na postawione powyżej pytanie powinna prawdopodobnie brzmieć tak, jeśli w danym konkretnym zastosowaniu może ona pomóc czytelniej wizualizować dany proces. Pozostaje zatem kwestią otwartą, czy i w jakim stopniu ten rodzaj wizualizacji znajdzie zastosowanie. Być może, wraz ze wzrostem zainteresowania obrazem stereoskopowym w kinematografii, twórcy oprogramowania także umożliwią tego rodzaju wizualizację, jako przynajmniej równoprawną tradycyjnej grafice. Literatura [1] Dokumentacja biblioteki QGLViewer: http://www.libqglviewer.com/ [2] Donald Urquhart, Swiftless Tutorials. Game Programming and Computer Graphics Tutorials: http://www.swiftless.com/about.html [3] Russel Smith, Open Dynamics Engine User Guide 8
Rysunek 4: Działanie stworzonej aplikacji: tryb 3D Rysunek 5: Działanie stworzonej aplikacji: tryb 2D 9