Přídavné ořezávací roviny
V minulých dvou dílech jsme si popsali funkce, které se v knihovně OpenGL používají pro nastavení kamery. Tyto funkce jsou v základní verzi knihovny OpenGL dvě, přičemž existuje jejich rozšíření v podobě funkcí z nadstavbové knihovny GLU:
- pro ortogonální režim kamery (tj. režim, ve kterém se používá rovnoběžné promítání) se používá funkce glOrtho() (poskytovaná přímo knihovnou OpenGL) nebo její jednodušší varianta gluOrtho2D() (ta je z nadstavbové knihovny GLU).
- pro perspektivní režim kamery se používá funkce glFrustum() (poskytovaná knihovnou OpenGL), ta je však často nahrazena voláním funkce gluPerspective() (z nadstavbové knihovny GLU).
Na těchto funkcích je důležité, že kromě nastavení kamery (tj. transformační matice Projection, viz. díly OpenGL XIII a OpenGL XIV) se navíc nastavuje šest ořezávacích rovin, které vytvářejí zobrazovaný podprostor, který se nazývá těleso záběru (view volume). Těleso záběru má v případě ortogonálního režimu tvar osově orientovaného kvádru, v případě perspektivního režimu se jedná o komolý jehlan. Tato dvě tělesa jsou omezena šesti rovinami, které podle jejich orientace ke kameře nazýváme left, right, top, bottom, near a far. Obrázky, ve kterých byl tvar tělesa záběru zobrazen, byly prezentovány v minulém a předminulém dílu tohoto seriálu, pro jistotu si je však oba ještě jednou zobrazíme:
Obrázek 1: Šest základních ořezávacích rovin při rovnoběžném promítání
Obrázek 2: Šest základních ořezávacích rovin při perspektivním promítání
Ořezávání těmito šesti rovinami probíhá tak, že se souřadnice vrcholů nejprve transformují pomocí ModelView matice, čímž dostaneme tzv. eye coordinates. Poté se na tyto nové souřadnice aplikuje transformace pomocí projekční matice Projection, čímž získáme tzv. clip coordinates. Teprve s těmito souřadnicemi se provádí test na polohu transformovaného vrcholu vůči šesti základním ořezávacím rovinám. Důvod je ten, že tyto testy jsou v clip coordinates mnohem jednodušší než pro zadávané souřadnice vrcholů. Testy se zde zredukují na pouhé porovnávání dvojice čísel.
Kromě těchto šesti ořezávacích rovin je však možné vytvořit a povolit i další ořezávací roviny. Pomocí těchto ořezávacích rovin lze vytvořit další řezy vykreslovaným tělesem a odhalit tak jeho vnitřní strukturu, což se hodí například při tvorbě technických výkresů (každý pravděpodobně někdy viděl výkres nebo prospekt s 3D řezem automobilu, motoru, kotle či jiného složitého či zajímavého zařízení).
Další ořezávací roviny se vytváří pomocí funkce glClipPlane(), která má následující deklaraci:
void glClipPlane( GLenum plane, const GLdouble * equation );
Význam jednotlivých parametrů:
- Pomocí parametru plane specifikujeme, která ořezávací rovina se zadává. V OpenGL můžeme používat několik ořezávacích rovin, z nichž každá má přiřazeno nějaké číslo. Toto číslo se v programech zadává symbolickou konstantou ve tvaru GL_CLIP_PLANEi, kde i nabývá hodnot od 0 do maximálního počtu rovin, který lze zjistit zavoláním funkce glGetIntegerv(GL_MAX_CLIP_PLANES, &promenna_int) (výsledek je uložen do proměnné promenna_int). V každé implementaci OpenGL by mělo být k dispozici alespoň šest dalších ořezávacích rovin, jejich konstanty jsou tedy v rozsahu GL_CLIP_PLANE0 až GL_CLIP_PLANE5. Je však možné, že některé implementace podporují více ořezávacích rovin (ve skutečnosti jsem více než dvě další ořezávací roviny nikdy nepotřeboval použít, čímž ale netvrdím, že se někdy nemohou hodit).
- V parametru equation je uložena adresa (tj. ukazatel) na pole čtyř hodnot typu GLdouble. Tyto čtyři hodnoty specifikují parametry A, B, C, D v implicitní rovnici roviny, která má tvar Ax+By+Cz+D=0. První tři prvky pole equation jsou tedy složkami normálového vektoru roviny n=[A, B, C], poslední prvek udává vzdálenost roviny od počátku. Implicitně jsou nastaveny všechny složky na nulu, tj. rovnice každé ořezávací roviny má tvar 0×+0y+0z+0=0.
Obrázek 3: Význam další ořezávací roviny definované pomocí funkce glClipPlane()
Po zavolání této funkce je implicitní rovnice roviny se zadanými koeficienty A, B, C a D transformována inverzní maticí ModelView a teprve s novými koeficienty jsou provedeny testy, je proto důležité, ve kterém místě programu (resp. pro jakou ModelView matici) je funkce glClipPlane() zavolána. Testy se provádí tak, že se do rovnice ořezávací roviny dosadí souřadnice vrcholu (tj. souřadnice x, y, z) a pravá strana rovnice se vyhodnotí. Pokud je výsledek nulový nebo kladný, vrchol leží uvnitř potenciálně zobrazovaného poloprostoru, v opačném případě leží vně a není zobrazen.
Kromě zadání koeficientů do rovnice ořezávací roviny je ještě zapotřebí povolit ořezávání touto rovinou. To provedeme zavoláním funkce glEnable() s parametrem GL_CLIP_PLANEi (i se samozřejmě mění podle toho, kterou ořezávací rovinu povolujeme). Opětovné zakázání ořezávání je možné provést zavoláním funkce glDisable() s tímtéž parametrem. Zavoláním funkce glIsEnabled() lze zjistit, zda je ořezávání danou rovinou povoleno, či zakázáno.
Funkci glClipPlane() není dovoleno volat uvnitř příkazových závorek glBegin() a glEnd().
Někdy je vhodné zjistit, jaké jsou parametry rovnice některé již zadané ořezávací roviny. Pro tento účel můžeme použít funkci glGetClipPlane(), která má následující deklaraci:
void glGetClipPlane( GLenum plane, GLdouble * equation );
První parametr má stejný význam jako u funkce glClipPlane(), druhý parametr je ukazatel na pole o čtyřech prvcích typu GLdouble, do kterého se uloží parametry A, B, C a D rovnice vybrané ořezávací roviny.
Následuje jednoduchý příklad, v němž je vytvořena a povolena jedna ořezávací rovina, která zaručuje, že jsou zobrazeny pouze ty části tělesa, jež mají y-ovou souřadnici kladnou, tj. rovnice ořezávací roviny má tvar: Ax+By+Cz+D=0 → 0×+1y+0z+0=0. Koeficienty jsou tedy nastaveny na hodnoty: A=0, B=1, C=0 a D=0:
GLdouble eqn[4] = {0.0, 1.0, 0.0, 0.0}; glClipPlane(GL_CLIP_PLANE0, eqn); glEnable(GL_CLIP_PLANE0);
Demonstrační příklady
V prvním demonstračním příkladu je po jeho spuštění pouze vypsáno, kolik ořezávacích rovin je možné použít v dané implementaci knihovny OpenGL, a parametry každé z nich (které by po inicializaci měly být nastaveny na nulu). Je využita konstanta GL_MAX_CLIP_PLANES a funkce glGetIntegerv(), glIsEnabled() a glGetClipPlane(). K příkladu je zdrojový kód i zdrojový kód s obarvenou syntaxí.
V druhém demonstračním příkladu je objekt na obrazovce ořezán rovinou x-z, která prochází počátkem. Ořezávání je možné vypnout nebo zapnout pomocí kontextového menu dostupného pravým tlačítkem myši. Tento příklad vznikl rozšířením posledního příkladu (číslo 28) uvedeného v minulém dílu tohoto seriálu. K příkladu je zdrojový kód i zdrojový kód s obarvenou syntaxí.
Ve třetím příkladu je možné pomocí kontextového menu (dostupného pravým tlačítkem myši) nastavovat parametry ořezávací roviny, tj. směr normály a posun roviny od počátku. Narozdíl od předchozího příkladu je však ořezávací rovina nastavena až po nastavení všech transformačních matic, což způsobuje změnu orientace této roviny vůči pozorovateli. K příkladu je zdrojový kód i zdrojový kód s obarvenou syntaxí.
Pro majitele pomalejších linek je zde k dispozici celý článek i s přílohami.