V dnešním pokračování seriálu o nadstavbové grafické knihovně GLU se budeme zabývat funkcemi, pomocí nichž je možné provádět projekci a zpětnou projekci bodů či vektorů z trojrozměrného světového prostoru do okna aplikace. Také si popíšeme funkci, pomocí níž je možné jednoduše nastavit perspektivní transformaci.
V předchozí části tohoto seriálu jsme si popsali funkci, pomocí které je možné nepřímo zadat projekční matici, která se používá pro specifikaci ortogonálního pohledu na zobrazovanou scénu. Dnes si popíšeme funkci pro jednoduchou specifikaci takzvané perspektivní transformace. Tento typ transformace je možné použít prakticky ve všech aplikacích trojrozměrné počítačové grafiky, protože vcelku věrně napodobuje fungování optického systému lidského oka a jejím výsledkem jsou poměrně realisticky vyhlížející trojrozměrné objekty i celé scény.
Funkce pro jednoduché nastavení perspektivní kamery se v nadstavbové knihovně GLU nazývá (jak jinak) gluPerspective() a má následující hlavičku:
void gluPerspective( GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar );
Význam parametrů této funkce:
Obrázek 1: Význam parametrů funkce gluPerspective()
Funkci gluPerspective() lze naprogramovat i s využitím základních funkcí grafické knihovny OpenGL. Příklad implementace této funkce byl převzat ze stránek http://nehe.gamedev.org, který jsem přeložil do češtiny (poznámky) a poněkud zjednodušil:
// ---------------------------------------------- // Vlastní implementace funkce gluPerspective() // bez použití nadstavbové knihovny GLU. // ---------------------------------------------- void myGluPerspective( GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar) { // známá konstanta PI je použita pro převody // úhlů mezi stupni a radiány const GLdouble pi=3.1415927; // poloviční vzdálenost x-ové a y-ové // ořezávací roviny GLdouble fw; GLdouble fh; // výpočet vzdálenosti od 0 (počátku souřadnic) // do y-ové ořezávací roviny fh=tan(fovy/180*pi)*zNear/2; // (funkce tan() pracuje s radiány) // výpočet vzdálenosti od 0 (počátku souřadnic) // do x-ové ořezávací roviny fw=fh*aspect; // nakonec zavoláme funkci glFrustum(), která // nastavení transformační matice provede za nás glFrustum(-fw, fw, -fh, fh, zNear, zFar); }
Poloviční vzdálenost od rovin jsme počítali proto, že funkce glFrustum() vyžaduje vzdálenosti zadané od počátku souřadnic, a ne vzájemné vzdálenosti ořezávacích rovin.
Použití perspektivní transformace je ukázáno v prvním a druhém demonstračním příkladu.
Pomocí funkce gluProject() lze provést přímou projekci bodu či vektoru zadaného ve světových souřadnicích (world coordinates) do souřadnic okna (screen coordinates). Tak lze velmi jednoduchým způsobem zjistit, do kterého pixelu na obrazovce se promítne právě zpracovávaný vrchol.
Předností této funkce je, že se neprovádí žádné vykreslování, tj. obsah všech bufferů ve framebufferu zůstane nezměněn, i když zadaný bod či vektor je zpracováván v grafickém vykreslovacím řetězci.
Funkce gluProject() má hlavičku:
GLint gluProject( GLdouble objX, GLdouble objY, GLdouble objZ, const GLdouble *modelViewMatrix, const GLdouble *projectionMatrix, const GLint *viewPort, GLdouble* winX, GLdouble* winY, GLdouble* winZ );
Kde jednotlivé parametry mají následující význam:
Pokud přímá projekce pomocí funkce gluProject() proběhne v pořádku, vrátí se hodnota GL_TRUE, v opačném případě se vrátí hodnota GL_FALSE.
Pomocí funkce gluUnProject() je možné provést zpětnou projekci souřadnic z okna (screen coordinates) do světových souřadnic (world coordinates). Tuto funkci je možné použít například při získání 3D souřadnic kurzoru myši - stačí načíst pozici kurzoru myši na obrazovce, přiřadit mu z-ovou souřadnici a pomocí zpětné projekce zjistit souřadnice kurzoru v prostoru, ve kterém se specifikují souřadnice vrcholů těles.
Po této operaci je možné jednoduše pomocí myši například hýbat s objekty, nebo měnit jejich vrcholy či řídicí body (u Bézierových ploch a ploch typu NURBS).
Funkce gluUnProject() má následující hlavičku:
GLint gluUnProject( GLdouble winX, GLdouble winY, GLdouble winZ, const GLdouble *modelViewMatrix, const GLdouble *projectionMatrix, const GLint *viewPort, GLdouble* objX, GLdouble* objY, GLdouble* objZ );
Jednotlivé parametry této funkce mají následující význam:
Pokud zpětná projekce pomocí funkce gluUnProject() proběhne v pořádku, vrátí se hodnota GL_TRUE, v opačném případě se vrátí hodnota GL_FALSE.
První demonstrační příklad je velmi jednoduchý. Po spuštění se nejprve nastaví transformační matice Projection a ModelView. Projekční matice je nastavena pomocí výše popsané funkce gluPerspective(). Poté se vykreslí trojrozměrný objekt - čajová konvička, přičemž je možné myší měnit parametry kamery, tj. obsah projekční matice.
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 2: Screenshot prvního demonstračního příkladu s pozměněným poměrem šířka/výška pohledu
Druhý demonstrační příklad navazuje na příklad první s tím rozdílem, že se pro nastavení projekční matice Projection nepoužívá funkce gluPerspective(), ale vlastní implementace pomocí funkce myGluPerspective(), jejíž zdrojový kód byl ukázán ve druhé kapitole této části.
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 3: Screenshot druhého demonstračního příkladu s pozměněným poměrem šířka/výška pohledu
V dalším pokračování tohoto seriálu si ukážeme, jakým způsobem je možné zjednodušit výše zmíněnou funkci gluProject(), která pro běžné použití vyžaduje příliš mnoho parametrů. Také si ukážeme použití zpětné transformace bodů ze souřadnic okna do 3D prostoru pomocí funkce gluUnProject() tak, aby nebylo nutné zadávat z-ovou souřadnici pixelu. Dopředu prozradím pouze to, že se při získávání chybějící z-ové souřadnice budou číst informace z framebufferu.
Zkomprimovaná verze tohoto článku i s přílohami a demonstračními příklady je uložena zde.