Framebuffer
Jak jsme si již řekli v minulé části tohoto seriálu, jsou při vykreslování pomocí OpenGL příkazů výsledná data uložena do takzvaného framebufferu. Framebuffer se skládá z několika dílčích bufferů, z nichž každý je určen pro jednu nebo více specifických operací, které lze při vykreslování scény provádět. Do framebufferu se zapisují nebo se z něho čtou takzvané fragmenty, což jsou pixely, které kromě své barvy a průhlednosti obsahují i informace o hloubce (vzdálenosti od kamery), popř. i další informace. Na každý buffer se můžeme dívat jako na paměť, v níž jsou uložena rastrová data (pixely), která mají podle určení bufferu svůj specifický význam. Buffer lze vymazat nebo přečíst do hlavní paměti počítače, popř. do něj data přímo zapsat, což se používá například při programování některých grafických efektů. Představu o cestě, kterou vykonávají fragmenty před vlastním vykreslením, nám dá následující obrázek:
Při práci s OpenGL můžeme přímo či nepřímo používat čtyři typy bufferů:
- Color buffer(s) – barvový nebo více barvových bufferů
- Depth buffer – paměť hloubky, nazývaný také Z-buffer
- Stencil buffer – paměť šablony
- Accumulation buffer – akumulační buffer
Při inicializaci grafického kontextu OpenGL (což je funkce platformově závislá, ale lze ji částečně nahradit například i pomocí GLUTu zavoláním funkce glutInitDisplayMode()) musíme zadat, které z těchto čtyř bufferů budeme používat a dále specifikovat jejich vlastnosti a bitovou hloubku. Při vlastním vykreslování pak můžeme měnit vlastnosti jednotlivých bufferů, popř. je průběžně povolovat a zakazovat, nelze je však již vytvářet nebo rušit.
V dalším textu si popíšeme všechny čtyři typy bufferů.
Color buffer(s)
V barvovém bufferu (nebo bufferech) je uložena barevná informace o vykreslované scéně. Jeden z barevných bufferů je vždy zobrazen na obrazovce a představuje výsledek našeho programátorského snažení. Barvový buffer je v nejjednodušším případě pouze jeden, avšak může jich být nainicializováno několik. V barevném bufferu mohou být jednotlivé pixely uloženy buď ve formátu RGB (true-color), nebo může být použit index-color (paletový) režim, kdy pixely neobsahují přímo barvu, ale pouze index do barevné palety.
Na naprosté většině grafických akcelerátorů lze vytvořit minimálně dva barevné buffery, které lze využít při animacích k takzvanému double bufferingu, tj. k vykreslování do neviditelného bufferu a současnému zobrazení druhého bufferu. Po vykreslení snímku se úloha barevných bufferů prohodí. Podrobněji se budeme doublebufferingem zabývat již v příští části tohoto seriálu. V případě double bufferingu jsou tyto dva barevné buffery nazývány přední buffer (front buffer) a zadní buffer (back buffer). V příkazech OpenGL, které se odkazují na jednotlivé buffery, se používají symbolické konstanty GL_FRONT a GL_BACK.
Některé implementace OpenGL, které umožňují generování stereo obrázků pro různá 3D výstupní zařízení (brýle, helmy, stereo monitory apod.), mohou zobrazovat současně dva barevné buffery, jeden pro pravé a druhý pro levé oko. Tyto buffery se také podle toho jmenují levý buffer a pravý buffer. V příkazech OpenGL se používají symbolické konstanty GL_LEFT a GL_RIGHT. Pokud se kromě generování stereo obrázků používá i double buffering, existují současně čtyři barevné buffery označované jako GL_FRONT_RIGHT, GL_FRONT_LEFT, GL_BACK_RIGHT a GL_BACK_LEFT.
Kromě toho je někdy možné vytvořit i další barevné buffery, které nemají svůj specifický účel, ale lze je použít například pro různé grafické efekty. Tyto buffery se označují symbolickými konstantami GL_AUXi, kde rozsah i je určen konktrétní implementací. Množství barevných bufferů je dáno paměťovou kapacitou grafického akcelerátoru, rozlišením obrazovky, barevnou hloubkou jednotlivých bufferů a množstvím dalších bufferů (depth buffer, stencil buffer, accumulation buffer), které se vytvářejí. Každý další vytvořený buffer navíc zmenšuje volnou paměť na grafickém akcelerátoru, která se používá pro ukládání textur a pro display listy. Proto je při inicializaci aplikace vhodné povolit pouze ty buffery, které jsou skutečně zapotřebí.
Depth buffer
Depth buffer, tedy paměť hloubky, je někdy také nazýván Z-buffer. Jedná se o paměť, v níž jsou většinou uloženy informace zajišťující vykreslení pouze viditelných částí těles, tj. vzdálenější plošky jsou překryty ploškami bližšími. Funkci a význam paměti hloubky lze však při vykreslování průběžně měnit, takže je možné naprogramovat některé zajímavé efekty, například zobrazení stínů.
V naprosté většině případů je funkce paměti hloubky nastavena tak, že se při rasterizaci testuje Z-ová hloubka (vzdálenost od stínítka obrazovky, ve skutečnosti se většinou testuje převrácená hodnota Z-ové hloubky, ale to je již interní věcí grafického akcelerátoru) vytvořeného fragmentu s hloubkou uloženou ve framebufferu na pozici vkládaného fragmentu. Pokud je Z-ová hloubka fragmentu menší než ve framebufferu, fragment se do framebufferu uloží a samozřejmě přepíše i Z-ovou pozici. Pokud je Z-ová hloubka fragmentu větší, je jasné, že je fragment ve výsledné scéně neviditelný, a proto je zahozen.
U depth bufferu je velmi důležitá jeho bitová hloubka. Pokud je například depth buffer pouze osmibitový (tak ho měly implementovány první grafické akcelerátory na PC), je možné rozlišit pouze 256 vzdáleností, což může vést k vytvoření vizuálních artefaktů ve scéně. V dnešní době se používají 16bitové a 24bitové hloubky, kde se množství artefaktů snižuje (avšak nastat mohou, typicky pokud vytvoříme dva trojúhelníky, které se částečně překrývají).
Stencil buffer
Stencil buffer neboli paměť šablony je používán pro určení, do kterých míst na obrazovce je povoleno vykreslování. Jedno z možných použití stencil bufferu je při vykreslování 2D grafického uživatelského rozhraní současně s trojrozměrnou scénou. Dalším, pokročilejším použitím stencil bufferu je algoritmus pro vykreslování zrcadlících ploch nebo pro tvorbu stereo obrázků na monitoru s použitím stereo brýlí (dva pohledy na scénu snímané ze dvou bodů zobrazené prokládaně na jedné obrazovce).
U stencil bufferu lze zadat relační operaci, která se provádí mezi hodnotou fragmentu a hodnotou uloženou ve stencil bufferu. Také je možné zadat, zda a popř. při jaké podmínce se budou měnit data ve stencil bufferu. U stencil bufferu většinou není bitová hloubka tak kritická jako v případě depth bufferu. Dokonce nám pro některé jednodušší úlohy postačuje jednobitová hloubka (typicky ořezání scény do nějakého obrazce a výše zmíněná tvorba stereo obrázků).
Použitím stencil bufferu se budeme podrobněji zabývat v dalších dílech tohoto seriálu.
Accumulation buffer
Accumulation buffer obsahuje, podobně jako color buffer, rastrový obrázek. Do akumulačního bufferu je však možno akumulovat (např. sečíst) více obrázků. Akumulační buffer se používá například při programování efektu rozmazání pohybem – takzvaný motion blur.
Funkce akumulačního bufferu je dostupná pouze při používání RGB (true-color) režimu. V index-color (paletových) režimech nelze akumulační buffer použít.
Vytvoření jednotlivých bufferů
Vytvoření jednotlivých bufferů je nutné provést při inicializaci aplikace. Vytvoření se děje při registraci tzv. vykreslovacího kontextu (rendering contextu) OpenGL. Tato registrace se většinou provádí zavoláním systémově závislých funkcí (v systémech Windows je to například dvojice funkcí ChoosePixelFormat() a wglCreateContext()), pokud však používáme knihovnu GLUT, můžeme použít systémově nezávislý příkaz glutInitDisplayMode(unsigned int mode). Ten provede vytvoření vyžádaných bufferů, není však možné specifikovat bitovou hloubku jednotlivých bufferů (což nám většinou nevadí).
Příkaz glutInitDisplayMode() má jeden parametr typu int, ve kterém pomocí bitových příznaků zadáme, které buffery se mají inicializovat. Jednotlivé bitové příznaky se samozřejmě nezadávají číselně, nýbrž pomocí symbolických konstant a operátoru or (v céčku |). Význam jednotlivých příznaků:
- GLUT_RGB – má se vytvořit jeden nebo více barvových bufferů v režimu RGB (true color), tj. barvy jsou zadávány třemi barevnými složkami (RGB – Red, Green, Blue) popř. i alfa složkou.
- GLUT_RGBA – má stejný význam jako GLUT_RGB.
- GLUT_INDEX – má se vytvořit jeden nebo více barvových bufferů v color-index (paletovém) režimu. Tento příznak se vylučuje s příznakem GLUT_RGB a GLUT_RGBA.
- GLUT_SINGLE – má se vytvořit pouze jeden barvový buffer. Pro rozlišení mezi color-indexovým a true color režimem se musí tento bit kombinovat s bity GLUT_RGB nebo GLUT_INDEX. Tento příznak se vylučuje s příznakem GLUT_DOUBLE a naopak.
- GLUT_DOUBLE – mají se vytvořit dva barvové buffery, tj. přední a zadní pro provádění double bufferingu. Podobně jako u předchozího bitu, i zde je nutno provést rozlišení mezi color-indexovým a true color režimem.
- GLUT_ALPHA – povolí vytvoření prostoru pro alfa složku v barvových bufferech. Pozor: povolení alfa složky je nutné provést nastavením tohoto bitu, použití pouze režimu GLUT_RGBA nestačí, i když k tomu název svádí. Alfa složku je možné použít pouze u barevného režimu RGB (true color).
- GLUT_ACCUM – má se vytvořit accumulation buffer. Má smysl pouze v barevném režimu RGB (true color).
- GLUT_DEPTH – má se vytvořit depth buffer (Z-buffer, paměť hloubky).
- GLUT_STENCIL – má se vytvořit stencil buffer (paměť šablony).
- GLUT_STEREO – používá se stereo výstup, tj. všechny barevné buffery se zdvojí.
- GLUT_MULTISAMPLE – používá se pro povolení režimu, ve kterém je na každý zobrazený pixel nutné vygenerovat více pixelů do framebufferu a poté se provede barevné sloučení. Tento režim většina běžných grafických akcelerátorů nepodporuje. Tímto způsobem lze implementovat velmi jednoduchýantialiasing.
- GLUT_LUMINANCE – používá se pro povolení zvláštního barevného režimu, kdy se nespecifikují barevné složky, ale pouze odstíny šedé. Podobně jako multisample režim, ani tento není na většině grafických akcelerátorů podporován.
Zjišťování informací o bufferech
Pokud používáme nadstavbovou knihovnu GLUT, můžeme pro zjištění informací o použitých bufferech použít funkci int glutGet(GLenum state), která vrací mimo jiné stav aktuálně nastaveného rendering contextu. Hodnoty parametrustate a význam hodnoty vrácené funkcí glutGet():
- GLUT_WINDOW_BUFFER_SIZE – bitová hloubka barvového bufferu
- GLUT_WINDOW_STENCIL_SIZE – bitová hloubka paměti šablony
- GLUT_WINDOW_DEPTH_SIZE – bitová hloubka paměti hloubky
- GLUT_WINDOW_RED_SIZE – počet bitů na červenou barevnou složku v barvovém bufferu
- GLUT_WINDOW_GREEN_SIZE – počet bitů na zelenou barevnou složku v barvovém bufferu
- GLUT_WINDOW_BLUE_SIZE – počet bitů na modrou barevnou složku v barvovém bufferu
- GLUT_WINDOW_ALPHA_SIZE – počet bitů alfa kanálu v barvovém bufferu
- GLUT_WINDOW_ACCUM_RED_SIZE – počet bitů na červenou barevnou složku v akumulačním bufferu
- GLUT_WINDOW_ACCUM_GREEN_SIZE – počet bitů na zelenou barevnou složku v akumulačním bufferu
- GLUT_WINDOW_ACCUM_BLUE_SIZE – počet bitů na modrou barevnou složku v akumulačním bufferu
- GLUT_WINDOW_ACCUM_ALPHA_SIZE – počet bitů alfa kanálu v akumulačním bufferu
- GLUT_WINDOW_COLORMAP_SIZE – velikost barevné palety v index-color režimu
- GLUT_WINDOW_DOUBLEBUFFER – nastaveno na 1, pokud je povolen double-buffering
- GLUT_WINDOW_RGBA – nastaveno na 1, pokud je nastaven RGBA (true-color) režim
- GLUT_WINDOW_STEREO – nastaveno na 1, pokud je povolen stereo barvový buffer
- GLUT_WINDOW_MULTISAMPLE – nastaveno na 1, pokud je povolen multisampling
Mazání bufferů
Mazání bufferů je operace časově náročná, protože je nutné projít všechny pixely v každém bufferu a nastavit je na nějakou hodnotu (většinou nulu). Proto je vhodné všechny buffery mazat současně, což umožňuje příkaz OpenGL void glClear(GLbitfield mask), kde pomocí parametru mask lze specifikovat jeden nebo více bufferů, které se mají smazat.
Parametr mask může být vytvořen logickým součtem hodnot definovaných v souboru gl.h: GL_COLOR_BUFFER_BIT (maže se zadní barvový buffer), GL_DEPTH_BUFFER_BIT (maže se paměť hloubky),GL_STENCIL_BUFFER_BIT (maže se paměť šablony) a GL_ACCUM_BUFFER_BIT (maže se akumulační buffer).
Typicky se příkaz glClear() používá před začátkem vykreslování scény. V demonstračních příkladech je to na začátku funkce onDisplay().
Výběr bufferu, do kterého se bude vykreslovat
Vykreslování lze provádět do některého z barvových bufferů: předního, zadního, levého předního, levého zadního, pravého předního, pravého zadního, nebo do libovolného dalšího (auxiliary) barvového bufferu. Pomocí příkazu void glDrawBuffer(GLenum mode) lze zadat, do kterého bufferu se budou provádět další vykreslovací operace. Parametr mode může nabývat těchto hodnot:
- GL_FRONT – přední buffer
- GL_BACK – zadní buffer
- GL_RIGHT – pravý buffer (není použit double buffering)
- GL_LEFT – levý buffer (není použit double buffering)
- GL_FRONT_RIGHT – přední pravý buffer
- GL_FRONT_LEFT – přední levý buffer
- GL_BACK_RIGHT – zadní pravý buffer
- GL_BACK_LEFT – zadní levý buffer
- GL_FRONT_AND_BACK – přední i zadní buffer současně (například vykreslení pozadí)
- GL_NONE – nevykresluje se do žádného bufferu
- GL_AUXi – jeden z přídavných barevných bufferů
Pro většinu klasických způsobů vykreslování je možné ponechat základní nastavení, tj. pokud není použit double-buffering, ponechat hodnotu GL_FRONT, a pokud je použit, vykreslovat do zadního bufferu (GL_BACK).
Demonstrační příklad
V demonstračním příkladu je ukázán způsob zjištění informací o jednotlivých bufferech. Po spuštění aplikace se stisknutím klávesy i provede výpis informací o bufferech. Změnou parametrů funkce glutInitDisplayMode()lze docílit jiné konfigurace bufferů. K dispozici je jak zdrojový kód příkladu, tak i zdrojový kód se zvýrazněnou syntaxí.
V příštím dílu tohoto seriálu si popíšeme vykreslování jednoduché prostorové scény s ortogonální kamerou.