Стенсил бафер

Овај чланак је добар. Кликните овде за више информација.
С Википедије, слободне енциклопедије
(преусмерено са Стенсилинг)
Пример примене стенсил бафера. Горња половина слике приказује слагање провидне равни преко тродимензионог објекта, чиме се постиже ефекат затамњења. Доња половина слике приказује како се стенсил бафер може употребити за прављење рупе у тој равни — иста се исцртава свуда осим на месту квадрата из стенсил бафера. Другим речима, тај квадрат је одузет од провидне равни током њеног цртања преко објекта.
Илустрација примене стенсил бафера примером из живота: два дела изрезане плочице се могу користити како да омогуће наношење боје унутар изрезане силуете, тако и ван ње.

Стенсил бафер (енгл. Stencil buffer, од Stencil што значи шаблон, шара, прошарица) је појам из области рачунарске графике. Означава бафер помоћу којег се сваком пикселу рачунарски исцртаване слике може доделити ознака у виду броја. Ово означавање се потом може користити за контролу начина на који ће појединачни пиксели бити исцртавани. Лаички речено, стенсил бафер је у рукама програмера алатка попут шаблон-лењира у рукама техничког цртача. Основна разлика је у томе што програмер овај шаблон може да обликује неограничен број пута и како год то погодује потребама исцртавања сцене. Прву хардверску имплементацију је представио Силикон Графикс, на манифестацији SIGGRAPH `88. Хардверска подршка за стенсил бафер данас постоји у оба интерфејса заступљена у индустрији рачунарских игара: у OpenGL, од прве верзије, и у Direct3D, од верзије 6.0.

Грађа и коришћење[уреди | уреди извор]

Стенсил бафер обично дели исти меморијски простор са дубинским бафером (енгл. Depth Buffer, Z-Buffer), а обично је тај однос 24 бита за дубински + 8 бита за стенсил бафер или, раније чешће коришћено, 15 бита за дубински + 1 бит за стенсил бафер. Такође је присутна варијанта 24 + 4, где се од 32 бита 28 користи, а 4 игнорише.[1] И стенсил и дубински бафер су део фрејм бафера (енгл. Frame Buffer), заједно за бафером за боју (енгл. Color Buffer). Први чип доступан ширем тржишту, који је подржавао један бит за стенсил бафер, био је 3Dlabs-ов Permedia II.

Битови додељени стенсил баферу се могу користити за репрезентацију бројевних вредности у опсегу [0, 2n-1], али и као n булових матрица (n је број додељених битова), од којих свака може да се користи за контролисање посебног дела сцене. Свака комбинација ова два начина коришћења расположиве меморије је такође могућа.

Стенсил тест[уреди | уреди извор]

Стенсил тест или стенсилинг (енгл. Stencil test, Stenciling) се међу операцијама над фрагментима (енгл. per fragment operations) налази након алфа теста и пре дубинског теста. Овим распоредом се омогућује да одговарајући пиксели, чији би приказ стенсил тест ионако спречио, ни не стигну до дубинског теста. Тиме се штеди време обраде сцене. На сличан начин, алфа тест може спречити одговарајуће пикселе да стигну до стенсил теста.

Сам тест се изводи над стенсил бафером како би се нека вредност у њему или изменила или употребила, а спроводи се кроз тзв. стенсил функцију и стенсил операције. Стенсил функција представља функцију којом се стенсил вредност одређеног пиксела пореди са задатом референтном вредношћу. Уколико је ово поређење логички тачно, стенсил тест пролази. У супротном не. Могуће стенсил функције су:

Значење OpenGL ознака[2] Direct3D ознака[3]
Никад не пролази GL_NEVER D3DCMP_NEVER
Пролази ако је вредност пиксела мања од референтне GL_LESS D3DCMP_LESS
Пролази ако је вредност пиксела мања или једнака референтној GL_LEQUAL D3DCMP_LESSEQUAL
Пролази ако је вредност пиксела већа од референтне GL_GREATER D3DCMP_GREATER
Пролази ако је вредност пиксела већа или једнака референтној GL_GEQUAL D3DCMP_GREATEREQUAL
Пролази ако је вредност пиксела једнака референтној GL_EQUAL D3DCMP_EQUAL
Пролази ако је вредност пиксела различита од референтне GL_NOTEQUAL D3DCMP_NOTEQUAL
Пролази увек. Ово је подразумевана функција. GL_ALWAYS D3DCMP_ALWAYS

Притом, могућа реакција условљена резултатом поређења разликује три стања дубинског и стенсил бафера:

  • Стенсил тест није прошао
  • Стенсил тест је прошао, али дубински тест није
  • Оба теста су прошла (или је стенсил тест прошао, а дубински није омогућен)

За сваки од ових случаја се може подесити другачија операција над испитиваним пикселом. Могуће стенсил операције су:

Значење OpenGL ознака[4] Direct3D ознака[5]
Задржавање тренутне вредности. Ово је подразумевана акција. GL_KEEP D3DSTENCILOP_KEEP
Постављање на нулу GL_ZERO D3DSTENCILOP_ZERO
Замена са референтном вредношћу GL_REPLACE D3DSTENCILOP_REPLACE
Увећање, али само ако је вредност мања од максималне могуће GL_INCR D3DSTENCILOP_INCRSAT
Увећање. Ако вредност премаши максималну могућу, постаје нула. - D3DSTENCILOP_INCR
Умањење, али само ако је вредност различита од нуле GL_DECR D3DSTENCILOP_DECRSAT
Умањење. Ако вредност треба да постане мања од нуле, постаје
максимална могућа.
- D3DSTENCILOP_DECR
Инвертовање на нивоу битова GL_INVERT D3DSTENCILOP_INVERT

Следи шематски приказ стенсил пајплајна[6]:

Следи опис ознака које већ нису поменуте:

  • Маска представља бројевну вредност којом се референтна вредност и вредност стенсил бафера за актуелни пиксел битовски конјугују пре него што ће бити подвргнути стенсил тесту.
  • Маска за стенсил операције представља низ од три операције дефинисане за сваки од наведених случајева (стенсил тест није прошао, стенсил тест је прошао али дубински није, оба теста су прошла). Само једна од ове три операције ће бити извршена над стенсил вредношћу актуелног пиксела.

У OpenGL се стенсил функција, референтна вредност и маска, тим редом, дефинишу функцијом glStencilFunc. У Direct3D се свака од ових компоненти подешава појединачно, помоћу методе SetRenderState уређаја који се тренутно контролише. Ова метода очекује по два параметра, од којих је први стање које се подешава а други његова вредност. Редом који је коришћен горе, ова стања се зову D3DRS_STENCILFUNC, D3DRS_STENCILREF и D3DRS_STENCILMASK.[7]

Стенсил операције се у OpenGL подешавају функцијом glStencilOp која очекује три вредности у већ наведеонм реду. Код Direct3D се, опет, свако стање посебно подешава методом SetRenderState. Три стања којима се могу доделити операције се зову D3DRS_STENCILFAIL, D3DRENDERSTATE_STENCILZFAIL и D3DRENDERSTATE_STENCILPASS.[7]

Примене[уреди | уреди извор]

Иако је спектар примена стенсил бафера прилично широк, може се навести неколико познатијих примена.

Проблем дубинске борбе[уреди | уреди извор]

На горњој слици се полигон са сликом налази тик изнад беле површи, што није довољно да се спречи ефекат дубинске борбе. Насупрот томе, код стенсилинга (доња слика) овај ефекат бива потпуно одстрањен, чак и када су површи копланарне.

Услед недовољне прецизности дубинског бафера, копланарни полигони који се налазе на малим растојањима, или се преклапају, бивају приказивани као два објекта са мноштвом неправилних пресека. Ти пресеци могу да варирају у зависности од положаја камере и других параметара, и да се јако брзо мењају. Ово се зове дубинска борба (енгл. Z fighting). Решења која могу да дају одређене резултате су:

  • Приближавање далеке равни за ограничавање сцене енгл. far clip plane, чиме се повећава прецизност дубинског бафера, али се смањује растојање на којем су објекти сцене видљиви.
  • Повећање броја битова додељених дубинском баферу, што је могуће на уштрб меморије за стенсил бафер.
  • Одмицање полигона, што би могло да се коси са захтевима обрађиване сцене.

Сви ови приступи проблему само смањују вероватноћу да ће до дубинске борбе доћи, и не гарантују дефинитивно решење у општем случају.

Решење које укључује стенсил бафер се заснива на знању који полигон треба испред којег да стоји. По овоме се силуета предњег полигона уцртава у стенсил бафер. Након тога се задњи полигон црта само тамо где силуета није уцртана, а остатак сцене може да се црта нормално.

Пример OpenGL имплмементације[уреди | уреди извор]

Пошто је ово уједно и најједноставнији пример, следи програмски кôд за OpenGL, који исцртава слику са доњег дела илустрације.

// ..

glEnable(GL_STENCIL_TEST);
  glDepthMask(0);
  glColorMask(0, 0, 0, 0);
    glStencilFunc(GL_ALWAYS,
                   1,
                   0xff );
    glStencilOp(GL_REPLACE,
                 GL_REPLACE,
                 GL_REPLACE );


    poligon_sa_slikom();


  glDepthMask(1);
  glColorMask(1, 1, 1, 1);
    glStencilFunc(GL_NOTEQUAL,
                   1,
                   0xff );
    glStencilOp(GL_KEEP,
                 GL_KEEP,
                 GL_KEEP );

    bela_povrs();
glDisable(GL_STENCIL_TEST);

poligon_sa_slikom();
Подесити камеру и остало.

Омогућити стенсил тест
  Закључати дубински бафер
  Закључати бафер за боју
    Подесити стенсил тест тако да увек
    пролази и, референтну вредност на 1.

    Подесити операције стенсил теста
    тако да се у њега пише референтном
    вредношћу где год се неки објекат
    исцртава.

    Исцртати полигон са сликом. Добија
    се његова силуета у стенсил баферу.

  Откључати дубински бафер.
  Откључати бафер за боју.
    Подесити стенсил тест тако да пролази
    кад год је вредност додељена актуел-
    ном пикселу различита од 1.
    Приликом овог пролаза се само чувају
    већ постојеће вредности.


    Исцртати белу површ.
Искључити стенсил тест.

Исцртати полигон са сликом.

Приликом закључавања и откључавања других бафера, коришћене су вредности 0 и 1 уместо, редом, константи GL_FALSE и GL_TRUE, зарад краћег записа.

Рефлексије[уреди | уреди извор]

Рефлексија неке сцене се исцртава као њен одраз у односу на дату раван, коју одређује полигон на коме рефлексија треба да се осликава. Део овог проблема који се односи стенсил бафер је ограничавање приказа рефлектоване сцене само на тај полигон тј. постизање илузије да се одраз сцене само на њему одражава. Стенсил бафер то омогућава на следећи начин:

  1. Сцена се исцрта без површи које представљају огледала
    • За свако огледало:
      1. Закључавају се дубински бафер и бафер за боју
      2. У стенсил бафер се уцрта видљив део огледала
      3. Дубински тест се подеси тако да за сваки прослеђени пиксел уписује максималну вредност и да увек пролази
      4. Исцртава се површина огледала
      5. Дубински тест се подеси тако да пролази само ако је удаљеност неког пиксела мања од тренутне (подразумевано понашање)
      6. Матрица трансформације се измени тако да рефлектује сцену у односу на раван огледала
      7. Откључавају се дубински бафер и бафер за боју
      8. Сцена се исцртава, али тако да се исцртава само њен део који се налази иза равни огледала, а видљив је на самом огледалу. За прво ограничење се додаје једна ограничавајућа раван (енгл. clip plane), а за друго ограничење се користи садржај стенсил бафера
      9. Опет се закључава бафер за боју, дубински тест се подешава тако да увек пролази, и пиксели огледала се бришу из стенсил бафера тако што се оно опет исцрта. Након овога је стенсил бафер опет чист и спреман за следеће огледало.

Уместо полигона, може се радити и о површи коју чини више некопланарних полигона. У том случају, ова површ се разбија на полигоне и сваки од њих се посматра као посебно огледало. Такође, овај пример помиње само савршена огледала, јер то не утиче на општост проблема. Део сцене који је означен као огледало се пре, током и након исцртавања рефлексије може дорађивати сваким другим алатом.

Један даљи проблем је огледање једног огледала у другом. Исти овај поступак се може поновити за одразе свих оваквих огледала. Економично решење може бити да се одраз огледала у огледалу увек исцртава као нерефлективна површина.

Раванске сенке[уреди | уреди извор]

Приликом цртања раванских сенки, доминантна су два проблема:

  • Први се односи на проблем дубинске борбе у случају да се раван геометријски не подели на део прекривен сенком и ван сенке. Видети одељак који се на ово односи.
  • Други проблем се односи на простирање сенке ван простора на којем раван постоји.

Још један проблем, који може а не мора да наступи, у зависности од технике, је пројектовање више полигона на један део сенке, што резултује тамнијим и светлијим деловима једне исте сенке. Сва три проблема се могу решити геометријски, но због могућности да се хардверско убрзање директно употреби, далеко је елегантнија имплементација помоћу стенсил бафера:

  1. Омогућити осветљење и сва светла
  2. Исцртати сцену без полигона на које треба пројектовати сенке
  3. Исцртати све полигоне на које треба пројектовати сенке, али без светала. Притом, у стенсил баферу, треба пикселима сваког полигона доделити вредност специфичну за полигон коме припадају. Размак између ових вредности треба да буде најмање два, јер ће за сваку раван бити коришћене две вредности за два могућа стања: у сенци и осветљено.
  4. Онемогућити свако глобално осветљење (како би се осигурало да на следеће кораке утиче само појединачно одабрано светло)
    • За сваку раван:
      • За свако светло:
        1. Изменити само стенсил бафер и само за пикселе који носе вредност специфичну за одабрану раван. Увећавају се вредности свих пиксела на које се пројектују објекти између дате равни и датог светла.
        2. Омогућити само одабрано светло и за њега исцртати део равни на којем за њу специфична вредност није била мењана.

Просторне сенке[уреди | уреди извор]

Стенсил бафер имплементација цртања просторних сенки представља сваку сенку геометријским телом, које својом запремином обухвата део сцене који се налази у њој. Уколико неки део сцене припада овој запремини, није осветљен датим светлом, у супротном јесте. Овај проблем се усложњава са порастом броја светала, али се не осврће на број површина на које сенке падају. Постоји више решења проблема, али сва следе следећи алгоритам:

  1. Нацртати сцену без икаквог осветљења
  2. Закључати дубински бафер и бафер за боју, тако да се на њима не могу вршити измене
    • За свако светло
      1. Користећи дубинску информацију о сцени (дубински бафер), попунити стенсил бафер само на деловима сцене где запремине сенки не постоје или нису видљиве од постојећих објеката.
      2. Откључати бафер за боју, и подесити функцију дубинског бафера тако да дозволи измене само тамо где је дубинска вредност једнака већ постојећој
      3. Исцртати ову сцену осветљену само овим светлом, али само за делове сцене који пролазе стенсил тест

Сваки од ових пролаза претпоставља да му на располагању стоји чисти стенсил бафер.

Како за сенке, ова техника се може користити и за осветљавање делова простора који се налазе под јаким светлом. На пример светлост рефлектора у тамној просторији са великим присуством прашине у ваздуху би видљиво осветљавала одговарајућу запремину простора.

Остале примене[уреди | уреди извор]

Један даљи пример су такозване меке сенке, код којих прелаз између осветљеног и осенченог дела сцене није оштар. Наиме, један начин да се овај ефекат постигне стенсил бафером јесте да се запремина сенке умножи, а да се њене копије редом скалирају по геометријском низу са малим увећањем, нпр. 1,04. Центар скалирања може да буде тежиште полигона који представља врх запремине. Ово ће само по себи дати низ композитних сенки које дају жељени ефекат.

Још једна примена обухвата поље визуелизације приликом моделирања техником конструктивне геометрије солида (енгл. Constructive Solid Geometry, CSG), где стенсил бафер, заједно са дубинским бафером, може успешно да решава проблеме Булових операција над солидима.

Види још[уреди | уреди извор]

Референце[уреди | уреди извор]

  1. ^ Tom Miller. Managed DirectX 9. стр. 61. ISBN 0-672-32596-9. 
  2. ^ Спецификација glStencilFunc (језик: енглески)
  3. ^ Спецификација D3DCMPFUNC (језик: енглески)
  4. ^ спецификација glStencilOp (језик: енглески)
  5. ^ Спецификација D3DSTENCILOP (језик: енглески)
  6. ^ Презентација о стенсил баферу, слајд 4 Архивирано на сајту Wayback Machine (7. јун 2008), документација на сајту NVIDIA(језик: енглески)
  7. ^ а б Спецификација равни стенсила, на MSDN (језик: енглески)

Литература[уреди | уреди извор]

  • Побољшавање сенки и рефлексија помоћу стенсил бафера, Марк. Џ. Килгард, NVIDIA Корпорација;

Спољашње везе[уреди | уреди извор]