4.2. Pronalazak parametara

 

            Prije ulaska u detaljno opisivanje kako algoritam radi, spomenimo nekoliko pravila koje će nam olakšati shvaćanje problema.

 

·        Kada neki poligon baci sjenu, nikoji drugi poligon ne može je izbrisati. Iz toga zaključujemo da usporedbena funkcija obilježavanja mora uspjeti kada vrijednost u spremniku za obilježavanje nije jednaka referentnoj vrijednosti izabrane za označavanje sjene. Kada je na nekom mjestu već postignuta ta vrijednost, funkcija usporedbe ne smije uspjeti. Dakle za funkciju usporedbe biramo NOTEQUAL referentnoj vrijednosti sjene.

·        Sjena mora biti bačena samo na onim mjestima gdje su Z-FAIL i STENCILPASS operacije bile izvršene zajedno (površina zasjenenog objekta unutar volumena sjene – područje C).

·        Gdje se Z-FAIL operacija obavi dvaput (unutrašnjost zasjenjenog objekta – područje D) vrijednost spremnika za obilježavanje mora biti jednaka nuli. Dakle, dvije Z-FAIL operacije moraju se međusobno poništiti (npr. za Z-FAIL operaciju uzmemo da invertira sadržaj spremnika za obilježavanje).

·        Gdje se STENCILPASS operacije obavi dvaput (područje između zasjenjenog objekta i objekta koji baca sjenu– područje B) vrijednost spremnika za obilježavanje mora biti jednaka nuli. Dakle, dvije STENCILPASS operacije moraju se međusobno poništiti, ali ne možemo koristiti istu operaciju kao i za Z-FAIL događaj.

·        Tamo gdje su se Z-FAIL i STENCILPASS operacije izvršile (područje sjene – područje C), redoslijed izvršavanja ne smije biti relevantan. Isti rezultat mora se postići za bilo koji redoslijed izvršavanja ovih operacija. Ovo pravilo je bitno jer budući da nema proračunavanja prednjih i stražnjih strana volumena sjene nikada ne možemo znati koja će se strana prva iscrtati.

·        Kako se obilježavanje zasjenjenih dijelova sjene izvodi samo u jednom koraku, algoritam ne smije praviti razliku između prednjih i stražnjih strana volumena sjene. Dakle, prilikom iscrtavanja volumena sjene moramo isključiti iscrtavanje samo pozitivno ili negativno orijentiranih poligona.

·        Vrijednost koja predstavlja sjenu u spremniku za obilježavanje mora biti jednoznačna. Zasjenjeni dijelovi moraju biti obilježeni jednom vrijednošću, a svi ostali s nulom.

Rješenje ovog problema leži u pronalasku pravih vrijednosti slijedećih parametara iscrtavanja:

 

STENCILFUNCTON           = NOTEQUAL

STENCILPASS                     = ?

Z-FAIL                                  = ?

SETNCILFAIL                      = ?

STENCILMASK                   = ?

SETNCILWRITEMASK      = ?

STENCILREF                       = ?

COLLMODE                        = NONE

ZFUNC                                 = ?

 

            Kao što smo prije napomenuli, dvije Z-FAIL operacije se moraju poništiti. To možemo postići odabirom INVERT operacije za ovaj događaj. Ako nulu obrnemo (invertiramo) dva puta opet ćemo dobiti nulu. Stoga zaključujemo:

 

STENCILZFAIL                   = INVERT

 

            Problem izbora STENCILPASS operacije malo je kompliciraniji. Dvije STENCILPASS operacije također se moraju poništiti, ali ne smijemo izabrati istu operaciju kao i za Z-FAIL događaj. Kombinacija STENCILPASS i Z-FAIL operacije mora postaviti spremnik za obilježavanje u neku fiksnu vrijednost koja predstavlja sjenu. Izabrali smo operaciju INCR (povećanje vrijednosti), ali ograničenu na samo prvi bit (invertiramo samo prvi bit). To postižemo na način da postavimo masku zapisivanja (WRITEMASK) u 11111101b tako da drugi bit nikako ne može postati 1. Ako pretpostavimo prazni spremnik za označavanje, a STENCILPASS operacija dogodi, spremnik će poprimiti vrijednost 1. Ako se opet dogodi STENCILPASS operacija, broj 2 će se pokušati zapisati, ali kako drugi bit ne može biti prebrisan, 0 će biti zapisana umjesto. Na taj način STENCILPASS operacija vrši invertiranje samo prvog bita. Da zaključimo:

               

                STENCILPASS                     = INCR

                STECILWRITEMASK         = 11111101b

                SETNCILMASK                   = 11111101b

 

            Ovi parametri zapravo rješavanju naš problem. Na onim mjestima gdje se dogodi STENCILPASS operacija zajedno s Z-FAIL operacijom spremnik za obilježavanje sadržavati će jednoznačnu vrijednost. Ta vrijednost jest 11111100b i označava dijelove sjene koje moramo zatamniti. Nju postavljamo kao referentnu vrijednost da bi spriječili njeno prerisavanje. Funkcija usporedbe će proći samo onda kada ta vrijednost nije postavljena.

      

STENCILREF                       =11111100b

 

            Demonstrirajmo ovo na jednostavnom primjeru za dva slučaja. U prvom slučaju vidljivom na slici 4.4., stražnja strana volumena sjene biva iscrtana prije prednje. U drugom slučaju, prikazanom na slici 4.5. prednja strana biva iscrtana prva.

 

a) Stražnja strana volumena sjene prva je iscrtana

b) Zatim je iscrtana prednja strana

c) Nakon toga, trokut koji sjeni ponovno je iscrtan da popravi vrijednost spremnika za obilježavanje na svom području.

 

Slika 4.4. Vrijednosti spremnika za obilježavanje kada se stražnja strana volumena sjene iscrtava prije prednje.

 

a) Prednja strana volumena sjene prva je iscrtana

b) Zatim je iscrtana stražnja strana

c) Nakon toga, trokut koji sjeni ponovno je iscrtan da popravi vrijednost spremnika za obilježavanje na svom području.

 

Slika 4.5. Vrijednosti spremnika za obilježavanje kada se prednja strana volumena sjene iscrtava prije stražnje.

 

Kao što možemo vidjeti, za svaki trokut koji baca sjenu sedam dodatnih trokuta volumena sjene mora biti iscrtano. Sedmi trokut je uveden da ispravi problem vlastitog sjenjenja. Ako je trokut koji baca sjenu okrenut prema svjetlu, sjena na njegovom mjestu ne smije postojati. U slučaju opisanom slikama 4.4. i 4.5. trokut je bio orijentiran prema svjetlu i prema promatraču tako da je u tom području imao samo stražnju stranu volumena sjene. Z-FAIL operacija se dogodila i spremnik za obilježavanje poprimio je vrijednost 1101b. Jedini način da se ovo ispravi jest da se još jednom obavi operacija Z-FAIL. Da bi iscrtavanje sedmog trokuta volumena sjene upravo to uzrokovalo, moramo osigurati za dubinska usporedba ne uspije. To postižemo postavljanjem usporedne funkcije dubinskog spremnika u LESS (obično je LESSEQUAL). Kada se ponovno iscrta taj trokut, z-test neće uspjeti i Z-FAIL operacija će se izvršiti. S druge strane, ako je trokut koji baca sjenu okrenut od svjetla, u njegovom području spremnik za obilježavanje treba poprimiti vrijednost sjene. U tom slučaju nedostajati će nam stražnja strana volumena sjene pa će se samo STENCILPASS operacija obaviti. Spremnik će tada imati vrijednost 0001b. Iscrtavanje sedmog trokuta uzrokovati će obavljanje Z-FAIL operacije i naš će trokut biti ispravno zasjenjen. Dakle, sedmi trokut volumena sjene isti je kao i trokut koji baca sjenu i služi kao nadopuna nedostajuće prednje ili stražnje strane volumena sjene. Da zaključimo:             ZFUNC = LESS

Cijeli postupak iscrtavanja zasjenjene sjene dan je slijedećim pseudo kodom:

 

ClearScene();

DrawScene();

SRS( ZWRITEENABLE,   FALSE );

SRS( ZFUNC,                                                 LESS );

SRS( STENCILENABLE,  TRUE );

SRS( SHADEMODE,                       FLAT );

// Postavljamo parametre oznacavanja

SRS( STENCILMASK,                     0xfffd );

SRS( STENCILWRITEMASK,          0xfffd );

SRS( STENCILREF,                        0xfffc );

SRS( CULLMODE,                          NONE );

for( each light )

{

  // Samo obiljezavamo scenu

  SRS( COLORWRITEENABLE, 0x0);            

  // Postavljamo parametre obiljezavanja

  SRS( STENCILZFAIL,   INVERT );               

  SRS( STENCILPASS,    INCR );

  SRS( STENCILFAIL,                     KEEP );

  SRS( STENCILFUNC,    NOTEQUAL);

 

  for( each object )

  {

      DrawShadowVolume();

  }

  // Zatamnimo oznacene djelove scene

  SRS( STENCILZFAIL,   KEEP );

  SRS( STENCILPASS,    ZERO );

  SRS( STENCILFAIL,      KEEP );

  SRS( STENCILFUNC,      EQUAL );

  SRS( COLORWRITEENABLE, 0xf );

  SRS( ALPHABLENDENABLE, TRUE );

  SRS( SRCBLEND,         INVSRCALPHA );

  SRS( DESTBLEND,        SRCALPHA);

  DrawBigGreyTransparentSquare();

  SRS( ALPHABLENDENABLE, FALSE );      

 // Obnovimo parametre iscrtavanja

  SRS( ZWRITEENABLE,     TRUE );

  SRS( STENCILENABLE,    FALSE );

  SRS( SHADEMODE,        GUARD );

}

 

Iz koda možemo vidjeti da nema proračunavanja oblika volumena sjene i da je volumen iscrtan kao svaka druga mreža. Kako je to omogućeno objašnjeno je u slijedećem poglavlju.