Obsah
1. Pomocná funkce pro aplikaci zpětné projekce2. Význam příkazů OpenGL použitých ve funkci myUnProject()
3. Zjednodušená pomocná funkce pro aplikaci zpětné projekce
4. Demonstrační příklady
5. Obsah dalšího pokračování
6. Seznam funkcí OpenGL a GLUT zmíněných v této části
7. Nové funkce z knihovny GLU popsané v této části
8. Zkomprimovaná verze článku i s přílohami
1. Pomocná funkce pro aplikaci zpětné projekce
V předchozí části tohoto seriálu jsme si popsali funkci gluUnProject(), pomocí které je možné provádět zpětnou projekci bodů ze souřadnic specifikovaných na obrazovce (screen coordinates) do souřadnic v původním trojrozměrném prostoru, ve kterém se zadávají souřadnice bodů a vektorů, jež vstupují do vykreslovacího řetězce (world coordinates).
Funkce gluUnProject() je však pro použití v reálných aplikacích zbytečně složitá, protože pro svou činnost potřebuje mnoho parametrů. Vzhledem k tomu, že některé parametry lze získat přímo ze stavového stroje OpenGL, je možné zpětnou projekci napsat jednodušeji pomocí vlastní funkce, která jako vstupy získá souřadnice [x, y, z] v prostoru obrazovky, a jako výstup vrátí souřadnice bodu ve světových souřadnicích [xw, yw, zw]:
Tato funkce může vypadat následovně:
// ----------------------------------------------- // Pomocná funkce pro aplikaci zpětné projekce. // Pro bod zadaný v souřadnicích obrazovky [x,y,z] // se vypočte odpovídající bod ve světových // souřadnicích [xw, yw, zw]. // ----------------------------------------------- void myUnProject( GLdouble x, // vstupy GLdouble y, GLdouble z, GLdouble *xw, // výstupy GLdouble *yw, GLdouble *zw) { // do těchto pomocných proměnných se uloží // souřadnice výsledného bodu GLdouble txw, tyw, tzw; // pole pro uložení aktuálně // nastaveného ViewPortu GLint viewPort[4]; // pole pro uložení aktuálně // nastavené matice ModelView GLdouble modelViewMatrix[16]; // pole pro uložení aktuálně // nastavené matice Projection GLdouble projectionMatrix[16]; // získat aktuální nastavení // ViewPortu glGetIntegerv(GL_VIEWPORT, viewPort); // získat aktuální nastavení // transformační matice ModelView glGetDoublev(GL_MODELVIEW_MATRIX, modelViewMatrix); // získat aktuální nastavení // transformační matice Projection glGetDoublev(GL_PROJECTION_MATRIX, projectionMatrix); // provést vlastní zpětnou projekci bodu gluUnProject(x, y, z, modelViewMatrix, projectionMatrix, viewPort, &txw, &tyw, &tzw); // předání parametrů - není nutné, // pokud se v předchozí funkci // přímo předají ukazatele xw, yw, zw, // zde je uvedeno pouze pro přehlednost *xw=txw; *yw=tyw; *zw=tzw; }
V dalším odstavci si popíšeme některé funkce OpenGL, které byly v předchozím příkladu použity a jejichž význam by mohl být z uvedeného kódu nejasný.
2. Význam příkazů OpenGL použitých ve funkci myUnProject()
Ve zdrojovém kódu funkce myUnProject() byly použity některé funkce ze základní grafické knihovny OpenGL, pomocí kterých je možné zjistit vnitřní stav (tj. parametry či atributy) stavového stroje OpenGL. Bližší informace o stavovém stroji OpenGL byly uvedeny v seriálu, který popisoval základy práce s
Mezi funkce, které byly použity v předchozím kódu, patří:
- glGetIntegerv(GL_VIEWPORT, viewPort) – pomocí této funkce je možné získat aktuální nastavení ViewPortu, pomocí kterého se provádí mapování a ořezání souřadnic pro zobrazení do okna aplikace. Po zavolání této funkce je naplněno pole viewPort, které obsahuje čtyři hodnoty typu GLint. Tyto hodnoty odpovídají souřadnicím [x, y] levého horního rohu obrazovky a pravého dolního rohu. Způsob nastavení těchto hodnot známe – používá se známá funkce glViewPort(), jejíž použití je patrné z demonstračních příkladů uvedených v této i v předchozích částech.
- glGetDoublev(GL_MODELVIEW_MATRIX, modelViewMatrix) – pomocí této funkce se získává nastavení prvků transformační matice ModelView. Tato matice obsahuje, stejně jako všechny ostatní matice, které slouží ke specifikaci transformace v homogenních souřadnicích [x, y, z, w], šestnáct prvků uspořádaných do čtyř řádků a čtyř sloupců. To znamená, že pole pro uložení hodnot prvků transformační matice musí mít kapacitu pro minimálně šestnáct položek typu GLdouble (který většinou odpovídá vestavěnému typu double použitého překladače).
- glGetDoublev(GL_PROJECTION_MATRIX, projectionMatrix) – tato funkce, podobně jako funkce předchozí, slouží k získání hodnot prvků transformační matice. U této funkce se však zjišťuje nastavení projekční transformační matice nazývané Projection. Vrácené hodnoty musí být uloženy do paměťové oblasti, která pojme alespoň šestnáct hodnot typuGLdouble resp. double (při překladu zkontrolujte, zda sizeof(GLdouble)==sizeof(double)!).
Přednosti výše uvedené funkce myUnProject() jsou zřejmé: v aplikaci není potřeba složitě alokovat pomocná pole a získávat prvky nastavených matic, vše se může provést automaticky a skrytě v jedné proceduře. Ve zjednodušování funkce pro zpětnou projekci však můžeme dále pokračovat, jak je ostatně naznačeno v následujícím odstavci.
3. Zjednodušená pomocná funkce pro aplikaci zpětné projekce
Výše uvedenou pomocnou funkci myUnProject() pro aplikaci zpětné projekce lze dále upravit tak, aby se nemusela složitě zjišťovat a zadávat z-ová souřadnice, jejíž hodnota nemá v ploše obrazovky význam, a proto ji nelze přímo zjistit (zjistit lze pouze x-ovou a y-ovou souřadnici, například z pozice kurzoru myši). Pro přečtení z-ové souřadnice bodu je však možné přečíst hloubku fragmentu na určeném místě na obrazovce.
Tato hloubka je, spolu s dalšími informacemi o fragmentech, uložená ve framebufferu, konkrétně v paměti hloubky (Z-bufferu, depth bufferu). Pro její přečtení se použije funkce pro čtení pixmapy z framebufferu, přičemž velikost této pixmapy se rovná pouze jednomu pixelu a souřadnice odpovídá zadaným souřadnicím x a y, tj.:
glReadPixels(x, y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &z);
Funkci glReadPixels() už známe, pro osvěžení pouze připomenu význam jednotlivých parametrů:
- V prvních dvou parametrech je předána souřadnice počátečního pixelu v pixmapě. Tato souřadnice v našem případě odpovídá souřadnicím bodu v ploše obrazovky, proto se zde předají hodnoty parametrů x a y.
- Ve třetím a čtvrtém parametru je specifikován rozměr pixmapy, kterou chceme z framebufferu přečíst. Vzhledem k tomu, že nás zajímá pouze hodnota jednoho pixelu, je horizontální i vertikální rozměr čtené pixmapy nastaven na jedničku, tj. w=h=1.
- Pátým parametrem je specifikováno, že se má pixmapa (resp. pouze jeden pixel – viz výše) přečíst z paměti hloubky. Pixmapu lze také přečíst z některého barvového bufferu, paměti šablony nebo z akumulačního bufferu, tyto funkce však v našem případě nevyužijeme.
- V šestém parametru je zadán datový typ jednotlivých pixelů, které budou z framebufferu přečteny a případně překonvertovány. Pro předání hodnot do funkcí gluProject() a gluUnProject() se používají datové typy GLdouble resp. GLfloat, proto je v šestém parametru funkceglReadPixels() specifikován symbolický identifikátor typu GLfloat.
- Poslední parametr by měl obsahovat ukazatel na pole, do kterého se přečtená pixmapa uloží. Vzhledem k tomu, že z framebufferu čteme hodnotu pouze jednoho pixelu, nemusí se pole vytvářet, stačí předat ukazatel na proměnnou typu GLfloat.
Zjednodušená verze funkce myUnProject() bude mít tvar:
// ----------------------------------------------- // Upravená pomocná funkce pro aplikaci zpětné // projekce. U této funkce není zapotřebí speci- // fikovat z-ovou souřadnici v prostoru obrazovky, // protože tato souřadnice je zjištěna z hloubky // uložené ve framebufferu. // ----------------------------------------------- void myUnProject2( GLdouble x, GLdouble y, GLdouble *xw, GLdouble *yw, GLdouble *zw) { // do těchto proměnných se uloží výsledný bod GLdouble txw, tyw, tzw; // proměnná, do které se z framebufferu // načte z-ová souřadnice pixelu GLfloat z; // pole pro uložení aktuálně // nastaveného ViewPortu GLint viewPort[4]; // pole pro uložení aktuálně // nastavené matice ModelView GLdouble modelViewMatrix[16]; // pole pro uložení aktuálně // nastavené matice Projection GLdouble projectionMatrix[16]; // získat nastavení ViewPortu glGetIntegerv(GL_VIEWPORT, viewPort); // získat nastavení // transformační matice ModelView glGetDoublev(GL_MODELVIEW_MATRIX, modelViewMatrix); // získat nastavení // transformační matice Projection glGetDoublev(GL_PROJECTION_MATRIX, projectionMatrix); // přečíst hloubku pixelu přímo z framebufferu // z paměti hloubky (Z-bufferu, depth bufferu) glReadPixels((GLint)x, (GLint)y, // pozice pixmapy 1, 1, // rozměry pixmapy GL_DEPTH_COMPONENT, // atribut fragmentu GL_FLOAT, // datový formát &z // výsledná "pixmapa" ); // provést vlastní zpětnou projekci gluUnProject(x, y, (GLdouble)z, modelViewMatrix, projectionMatrix, viewPort, &txw, &tyw, &tzw); // předání parametrů - není nutné, // pokud se v předchozí funkci // přímo předají ukazatele xw, yw, zw, // zde je uvedeno pouze pro přehlednost *xw=txw; *yw=tyw; *zw=tzw; }
4. Demonstrační příklady
Po spuštění prvního demonstračního příkladu se zobrazí známý trojrozměrný model čajové konvičky. Pomocí levého tlačítka myši je možné modelem rotovat, pravým tlačítkem se mění vzdálenost tělesa od pozorovatele.
Při pasivním pohybu myší (tj. bez stisknutého tlačítka) se odečítají souřadnice kurzoru na obrazovce a pomocí funkce myUnProject() se provádí přepočet těchto souřadnic do světového prostoru, přičemž je z-ová souřadnice v prostoru obrazovky nastavena na nulu (což není obecně korektní). Odečtené i vypočtené souřadnice jsou zobrazeny v titulkovém pruhu okna pomocí funkce glutSetWindowTitle().
Zdrojový kód prvního demonstračního příkladu je dostupný zde, jeho HTML verze se zvýrazněním syntaxe zde.
Obrázek 1: Screenshot prvního demonstračního příkladu
Druhý demonstrační příklad navazuje na příklad první s tím, že se souřadnice ve světovém prostoru počítají ve funkci myUnProject2() korektně, tj. z-ová souřadnice na obrazovce je získána čtením z framebufferu. Zkuste kurzorem myši přejet po hranici mezi černým pozadím ve scéně a zobrazeným objektem. Vzdálenost pozadí je nastavena na poměrně velkou hodnotu (zadní ořezávací rovina), kdežto body 3D objektu mají z-ovou hodnotu, která se pohybuje okolo počátku v rozsahu –2..2 jednotky.
Zdrojový kód druhého demonstračního příkladu je dostupný zde, jeho HTML verze se zvýrazněním syntaxe zde.
Obrázek 2: Screenshot druhého demonstračního příkladu
5. Obsah dalšího pokračování
V dalším pokračování seriálu o nadstavbové knihovně GLU si popíšeme funkce, které je možné využít pro vytvoření a následné zobrazení kvadrik, tj. těles ve tvaru koulí, válců a disků.
6. Seznam funkcí OpenGL a GLUT zmíněných v této části
glGetDoublev()glGetIntegerv()
glReadPixels()
glViewPort()
7. Nové funkce z knihovny GLU popsané v této části
gluProject()gluUnProject()
8. Zkomprimovaná verze článku i s přílohami
Zkomprimovaná verze tohoto článku i s přílohami a demonstračními příklady je uložena zde.