2011. november 5., szombat

OpenGL Stencil-puffer

Az OpenGL elsajátítása közben számos buffer használatával meg kell ismerkednünk a megfelelő grafikai hatás eléréshez. Ezek közül a legismertebb - és a legtöbbet használt is - a z-buffer, aminek a működését és fontosságát nem kell magyarázni senkinek sem. Mégis akad egy olyan tárrész az OpenGL-ben, amit az elején a legtöbb lelkes tanuló nem tud hova tenni, pedig óriási szerepe van a különféle grafikai hatások elérésben. Ez a tár pedig nem más, mint a stencil-buffer. Mivel számomra is sokáig megfoghatatlan volt, álljon most itt egy kis elméleti ismertető róla. Később persze gyakorlati példákon keresztül is megnézzük a működését.
 Bevezetés

A stencil buffer (magyarul sablon puffer) a modern számítógépes grafikai-hardveren alapuló extra puffer (vagyis tárolóegység). Arra használjuk, hogy a rajzolást a képernyő bizonyos részeire korlátozzuk. Ahhoz hasonló, mint amikor egy kartonlapba feliratot vágunk és a lapot egy felületre helyezve lefestjük, aminek során a felületre csak a felirat kerül. A fénymásolok elterjedése előtt ezt a technikát használták kis példányszámú sokszorosításra. Más hasonlattal élve a stencil puffer használatával tetszőleges alakú ablakon át nézhetjük a világot. Az egyik lehetséges alkalmazás a vetett árnyékok megjelenítéséhez használja ezt a lehetőséget, mint például a Doom 3, a Star Wars Galaxies vagy a Blade nevű videojátékok. [Wikipedia]

OpenGL Stencil Shadow and Reflection

A stencil tár ismertetése

A stencil-teszt a z-buffer használata előtt történik meg. A stencil buffer működését a z-bufferhez hasonlóan ki és be lehet kapcsolni.  A stencilvizsgálatokat csak akkor hajtja végre a rendszer, ha van stencilpuffer, egyébként a fragmentumok ezen a ponton mindig túljutnak. A vizsgálat abból áll, hogy a rendszer összehasonlítja a fragmentumnak megfelelő pixelhez a stencilpufferben tárolt értéket egy referenciaértékkel, és az összehasonlítás eredményétől függően módosítja a pufferben tárolt értéket. A referenciaértéket és az összehasonlító függvényt a glStencilFunc, a módosítást pedig a glStencilOp paranccsal adhatjuk meg.

void glStencilFunc(GLenum func, GLint ref, GLuint mask)

A stencilvizsgálatnál használt összehasonlító függvényt (func), referenciaértéket (ref) és maszkot (mask) állítja be. A rendszer a referenciaértéket a func függvény szerint összehasonlítja a stencilpufferben tárolt értékkel, de annak csak azokat a bitjeit veszi figyelembe, amelyhez tartozó bitnek a mask-ban 1 az értéke. Ha a stencilpuffernek s darab bitsíkja van, akkor a rendszer az összehasonlítás előtt a stencilpufferbeli értéket és a mask s darab kisebb helyiértékű bitjét logikai és (AND) műveletbe hozza, és ennek eredményén végzi el az összehasonlítást. A func paraméter lehetséges értékeit az alábbi felsorolás tartalmazza, a listában val a fenti bitenkénti AND művelet eredményét jelöli. A stencilvizsgálathoz használható összehasonlító függvények:
  • GL_NEVER – soha nem engedi tovább a fragmentumot
  • GL_ALWAYS – mindig továbbengedi a fragmentumot
  • GL_LESS - továbbengedi ha ref < val
  • GL_LEQUAL – továbbengedi ha ref val
  • GL_EQUAL – továbbengedi ref = val
  • GL_GEQUAL – továbbengedi ha ref val
  • GL_GREATER – továbbengedi ha ref > val
  • GL_NOTEQUAL – továbbengedi ha ref val
Alapértelmezés szerint func = GL_ALWAYS, ref = 0 és mask minden bitje 1.
A z-buffer algoritmus használatakor kétféle döntés születhet: a sokszöget az adott pixelben a Z értéke alapján nem rajzoljuk ki, vagy pedig mélységértékét beírjuk a z-bufferbe, színértékét pedig a rasztertárba. Mivel a stencil-teszt megelőzi a z-buffer használatát, így a két buffer minden pixelre együttesen háromféle eredményt adhat:
  • a stencil-teszt sikertelen
  • a stencil-teszt sikerül, de a z-buffer nem engedi a rajzolást
  • a stencil és a z-buffer alapú teszt is sikerül
Az OpenGL-ben – a glStencilOp függvény segítségével – mindhárom eredményéhez megmondhatjuk, hogy a stencil bufferbeli értékkel mit tegyen.

void glStencilOp(GLenum fail, GLenum zfail, GLenum zpass)

A függvény segítségével azt adhatjuk meg, hogy a rendszer hogyan módosítsa a stencilpuffert amikor a fragmentum továbbmegy vagy fennakad a vizsgálaton. A fail, zfail és zpass paraméterek értékei az alábbi lista szerintiek lehetnek:
  • GL_KEEP – megtartja a kurrens értéket
  • GL_ZERO – 0-val felülírja a kurrens értéket
  • GL_REPLACE – a referenciaértékkel írja felül
  • GL_INCR – eggyel növeli a kurrens értéket
  • GL_DECR – eggyel csökkenti a kurrens értéket
  • GL_INVERT – bitenként invertáltja a kurrens értéket
Inkrementális és dekrementálás után a rendszer a kapott értéket a [0, 2^s - 1] intervallumra levágja, ahol s a stencilpuffer bit/pixel mérete. A fail paraméterrel megadott függvényt akkor használja a rendszer, ha a fragmentum nem jut túl a stencilvizsgálaton; a zfail-el megadottat akkor, ha a stencilvizsgálaton túljut, de a mélységvizsgálaton nem; a zpass-al megadottat pedig akkor, ha a stencilvizsgálaton túljut és nincs mélységvizsgálat, vagy van, de azon is túljutott. Alapértelmezés szerint fail = zfail = zpass = GL_KEEP.
A következő példában arról rendelkezünk, hogy ha a stencil-teszt sikertelen, akkor a buffer értékét lenullázzuk. Ha a stencil buffer alapú teszt sikerül, de a z-buffer alapú nem, akkor eggyel csökkentjük, egyébként pedig az értéket változatlanul hagyjuk:

void glStencilOp(GL_ZERO, GL_DECR, GL_KEEP)

A stencilvizsgálatot a glEnable(GL_STENCIL_TEST) paranccsal engedélyezhetjük, a glDisable(GL_STENCIL_TEST) paranccsal letilthatjuk, a glIsEnabled (GL_STENCIL_TEST) paranccsal pedig lekérdezhetjük, hogy engedélyezett-e. A stencilvizsgálatokkal kapcsolatos beállításokat a lenti felsorolás paramétereivel kiadott glGetlntegerv() paranccsal kérdezhetjük le:
  • GL_STENCIL_FUNC – stencil függvény
  • GL_STENCIL_REF – stencil referenciaérték
  • GL_STENCIL_VALUE_MASK – stencil mask
  • GL_STENCIL_FAIL – fail függvény
  • GL_STENCIL_PASS_DEPTH_FAIL – zfail függvény
  • GL_STENCIL_PASS_DEPTH_PASS – zpass függvény
A stencilvizsgálat leggyakoribb alkalmazása, hogy a képernyő tetszőleges alakú területét lemaszkoljuk, azaz nem engedjük, hogy oda rajzoljon a rendszer. Ehhez előbb töltsük fel a stencilpuffert 0-val és rajzoljuk meg a kívánt alakot a stencilpufferbe 1 értékkel. Közvetlenül nem lehet a stencilpufferbe rajzolni, de a következő eljárással közvetett módon ugyanazt az eredményt kapjuk:
  • a színpufferbe zpass GL_REPLACE beállítás mellett rajzoljunk;
  • a színmaszkot állítsuk 0-ra (vagy GL_ZERO), hogy a színpufferben ne történjen változás;
  • a mélységpuffert tegyük csak olvashatóvá, ha nem akarjuk, hogy tartalma változzon.
A stencilterület megadása után állítsuk a referenciaértékeket 1-re, és az összehasonlító függvényt olyanra, hogy a fragmentum akkor jusson túl, ha a stencilsík értéke megegyezik a referenciaértékkel. Fontos, hogy rajzolás alatt ne változtassuk a stencilsíkok tartalmát!

Befejezés

A bejegyzés elég szárazra sikerült, de remélem, jó támaszt nyújt majd a gyakorlati problémák megoldásához. A bejegyzésben sokat használtam fel Juhász Imre: OpenGL c. jegyzetéből ami rendkívül jó alapot nyújt az OpenGL megismeréséhez. Mindenkinek csak javasolni tudom. A következő részben már gyakorlatorientáltak leszünk. Ígérem.

1 megjegyzés :

  1. Watch: 'Nba Jam' Live Stream | Vimeo
    Listen to Nba Jam live stream live on Vimeo. The youtube mp4 basketball tournament has won 7,500+ fans. Get an in-depth look at the 2020 NBA season and

    VálaszTörlés