D y n a m i c k é    s t í n y       
                       DDDyynnaammiicckkéé  sssttíínnyy      
                        D y n a m i c k é    s t í n y       
                                                             

            Pokud   alespoň   trochu  sledujete  demoscénu,  určitě  vám
       neuniklo,  že  se  začínají  rozmáhat  všemožné simulace reálných
       světelných  efektů,  povětšinou v podobě rychlého raytracingu bez
       časově náročného osvětlovacího modelu. Programátoři těchto enginů
       mají  většinou  v  plánu co nejlepší emulaci reálných dynamických
       stínů.   Obecně  existuje  relativně  mnoho  způsobů,  jak  stíny
       generovat. Mezi časově nejnáročnější a zároveň nejpřesnější patří
       samozřejmě  radiosita  implementovaná metodou konečných elementů.
       Pokud  hrajete  nějaké nejnovější 3D super hity, určitě jste její
       výsledky  viděli  v  praxi.  Její  přesnost  je zejména patrná na
       plynulých  přechodech  mezi  světlem  a  stínem  v  závislosti na
       vzdálenosti  od světelného zdroje. Člověk má opravdu pocit, že to
       světlo  tam  opravdu  je.  Ostatně není divu, radiosita spojená s
       Monte  Carlo  raytracingem  je  v  současné době asi to nejlepší,
       čím  můžeme  výpočetní  technikou  simulovat realitu. Jenomže pro
       takové  hrátky  v  reálném  čase  nám  většinou  nestačí  ani  ty
       nejrychlejší  superpočítače  dneška.  Všechno  se  musí  spočítat
       dopředu, a pak staticky shadow-texturovat za běhu. Stíny jsou tím
       pádem statické a hodí se pouze na poziční světelné zdroje.

            Další   možnost,   co   do  reálnosti  stínů  velmi  podobná
       radiositě, je již zmiňovaný raytracing. S přechody světlo stín už
       to  není  tak  růžové,  ale  například pro tzv. spotlights (např.
       baterka) nebo openlights (např. lucerna zavěšená relativně daleko
       od  stěn)  je  vizuální  rozdíl  takřka zanedbatelný. Navíc pokud
       použijeme  pouze geometrický model, tzn. nepočítáme žádné barevné
       efekty,  mnohonásobné odrazy a další vymoženosti raytracingu, lze
       jej  urychlit  do  takové míry, že dnešní P2 zobrazují jednodušší
       scény  v  reálném  čase.  Je třeba podotknout, že mnoho enginů od
       Transgression  2, přes  Sink  po  Gateways  používá mnohdy značně
       redukované  rozlišení,  díky kterému snižují počet vystřelovaných
       paprsků a výsledek interpolují do výsledného rozlišení videomódu.
       Všechno je pak dosti zubaté a rozmazané.

            Jak  vidíte, ani tento způsob není úplně ideální. Dalo by se
       říci, že je to taková honba s tankem na jelena, protože to, co za
       takovýchto   okolností   oddře   raytracing,   lze   velmi  dobře
       aproximovat  analyticky.  Získáme  opravdu mnohonásobné urchylení
       bez  významných  změn  na  přesnosti  a vizuálním dojmu. Navíc si
       můžeme  dovolit  vyšší  rozlišení  a  rychlejší  fps,  což  velmi
       pozitivně  působí  na diváka. Lidské oko je k plynulým pohyblivým
       světelným  efektům mnohem shovívavější, a tak si můžeme dovolit i
       menší   nepřesnosti.  Je  tu  ovšem  jedna  nevýhoda.  Musíme  se
       orientovat  pouze  na  spotlight.  Hlavní finta, která nám umožní
       velmi  rychlou  simulaci  stínů,  je  rendering  scény  z pohledu
       světla.

            První  demo, které toho využilo společně s texturováním, je,
       pokud  si dobře pamatuji, Magic View od Dubiusu (vzpomínám si, že
       před  Magic  View  bylo  ještě jedno demo, které analytické stíny
       mělo,  ale  pouze  ve  flat  podobě  na  nikterak  složité děravé
       krychli).  Tak  jako  tak  lze říci, že Tremor je v tomto oboru v
       mnoha  směrech  na  demoscéně  pionýrem.  Pokud  jste  Magic View
       viděli,  určitě  si  pamatujete  na  scénu  s  děravou krychlí na
       podstavci  osvětleném  dvěma  kuželi světla. Nedám za to krk, ale
       řekl  bych,  že  Tremor  používá  právě analytickou metodu. Je to
       totiž  patrné  ze  všeobecně  velké  přesnosti  a absence chyb, o
       kterých se zmíním později. Také mírně hranatý kužel upozorňuje na
       analytickém  ořezávání.  Prostě  princip  bude  stejný.  Scénu je
       třeba  přetransformovat  pomocí  matice  světla, která je podobná
       transformační   maticí   kamery  (funkčně  naprosto  stejná),  do
       pohledu  světla  a  jednoduše  malířem  (pokud je k dispozici BSP
       strom,  je  situace  mnohem  jednodušší) "vykreslovat" jednotlivé
       polygony.  Nejde tak ani o to je vykreslit, ale zpracovávat je ve
       správném  pořadí,  čímž  se  zajistí jejich korektní viditelnost,
       která  je  pro  vytvoření  reálných  stínů  velmi  podstatná. Při
       "vykreslování"  následujícího  polygonu  je  třeba  pomocí rovin,
       které  jsou  definované  hranou  tohoto  polygonu a bodem světla,
       osekat  polygony,  které  ten  nový částečně překryje. Jedná se o
       naprosto  identické  ořezávání  jako  v  případě kamery. Může tak
       například  dojít  k  tomu,  že  původní  vzdálenější polygon bude
       postupně  rozsekán  na  několik disjunktních částí. Je nutné tyto
       části  stále udržovat ve spojovém seznamu, na jehož kořen ukazuje
       pointer  z  původního  netransformovaného a neořezaného polygonu.
       Každá   tato  část  se  ale  stává  pro  další  krok  samostatným
       polygonem.   Ty  polygony,  které  se  nachází  uvnitř  ořezávací
       "pyramidy",  se  logicky  odhazují. Pokud znáte span buffery, tak
       lze  říci,  že  se jedná o velmi podobnou myšlenku, rozšířenou na
       celé  polygony.  Následuje  rendering  z  pohledu  kamery.  Kromě
       stávajících  polygonů je třeba do transformací zapojit i polygony
       vzniklé  při  ořezávání  z  pohledu  světla.  Budou  sloužit jako
       indikátory  světla  a  stínu.  V praxi je třeba před texturováním
       polygonu  vykreslit  jeho  jakýsi  2D  shadow  buffer  z  pohledu
       kamery,  jenž  je  analyticky  přesný,  a z něho pak při samotném
       texturování  odečítat  hodnotu  světla  a  stínu.  Je  zřejmé, že
       přechod  světlo/stín  je  "hranatý", ale o tom pohovořím později.
       Důležité  je, že tuto metodu lze všelijak modifikovat a docilovat
       velmi  značného  urychlení,  zejména  při  použití BSP stromů. Na
       scénu  s  malým počtem polygonů je opravdu velmi efektivní a dává
       analyticky  naprosto přesné výsledky. S rostoucím počtem polygonů
       efektivita  značně klesá a analytická přesnost a striktní ostrost
       stínů  lze  v  mnoha případech oželet. Jak jistě tušíte, přichází
       další urychlení a zjednodušení problému.

            Dostávám  se tak, po poněkud delším úvodu do problematiky, k
       jádru   tohoto   článku.   V  současné  době  asi  nejrychlejší a
       nejjednodušší  algoritmus  generující  dynamické  stíny v reálném
       čase  s  dostatečnou "diváckou" přesností je technika, která se v
       odborných  kruzích  nazývá  Shadow  map,  případně Poly-Id Shadow
       buffer.  Poprvé  s  ní přišli pánové od SGI v podobě příspěvku do
       SIGGRAPHu  '87.  Na poli demoscény se ale objevila až s příchodem
       vynikajícího  64kB  intra  Spotlite od dnes již velmi populárního
       Digisnapa.  Spotlite  využívá  tuto  techniku  na  plné obrátky a
       zároveň  ukazuje  v  plné kráse její efektivitu a rychlost (intro
       běželo  plynule  i  na  486  !). Díky simulaci plynulého přechodu
       světlo/stín   vypadá   občas   velmi  podobně  jako  shadow  mapy
       vygenerované  pomocí  radiačních  metod,  což  je  opravdu  velmi
       pozitivní  efekt.  Digisnap  jej  s  úspěchem  použil  i  ve svém
       posledním  trháku  Fulcrum.  Ale nic není jednoduché, jak by se z
       počátku mohlo zdát. Jak to tedy všechno vlastně funguje ?

            Princip  je podobný jako u analytických stínů. Scénu nejprve
       vykreslíme  z  pohledu světla, a to tak, že do bufferu, nejlépe o
       stejných   rozměrech   jako   výstupní   rozlišení,  vykreslujeme
       jednotlivé  polygony,  jako  kdybychom  chtěli  klasicky zobrazit
       scénu  ve  flat  stínování.  Místo  barvy  ovšem  použijeme index
       polygonu.  Je  nutné  za prvky bufferu alokovat integery, protože
       složitější  scény  mnohdy několikrát převyšují limit 254 polygonů
       (index 0 slouží jako disjunktní činitel, tedy v podstatě globální
       tma;  její  výhody ještě upřesním). Je také dobré polygony, které
       jsou  normálou natočeny od světla, nebo ty, na které kužel světla
       ani nedopadne, vykreslit jako neosvětlené. Následuje vykreslování
       z pohledu kamery. Ještě než začne, je dobré vykreslit do aktuálně
       vygenerovaného  shadow bufferu inverzní kružnici indexem 0, která
       zajistí   výsledný   tvar  kužele.  Nikomu  nebráním  tuhle  fázi
       vynechat,  ale  kdo  z  vás  kdy viděl čisté obdélníkové světlo ?
       Podívejte  se  na  obrázek , který zachycuje shadow buffer, jehož
       aplikaci můžete zhlédnout na dalším screenshotu. O postproduktech
       a  všeobecné variabilitě v této fázi algoritmu pohovořím později.
       Zato   nesmíme   zapomenout   zachovat   transformační   matici z
       předchozího   průchodu   i   do  této  fáze,  protože  přijde  ta
       nejdůležitější   část  celého  algoritmu.  Postupujeme  klasicky.
       Ořezáme  všechny  viditelné polygony a transformujeme jejich body
       z  objectspace  (za  space  se  zde  považuje  relativní souřadný
       systém)  do  cameraspace,  spočítáme 2D souřadnice jejich vrcholů
       a  pomocí transformační matice z předchozího průchodu spočítáme i
       jejich    souřadnice    [Xs,Ys,Zs]    v   lightspace.   Následuje
       vykreslování samotného polygonu. Kromě informací o textuře (např.
       U a V) zavedeme lineární interpolaci souřadnic [Xs,Ys,Zs] tak, že
       je   násobíme   převrácenou   hodnotu   Z-ové  komponenty  bodu v
       cameraspace.   Vše   interpolujeme  naprosto  identicky  jako  2D
       souřadnice (např.  texely  U  a  V).  Při  samotném  vykreslování
       scanline  postupujeme  také  identicky.  Například každý 16 pixel
       provedeme  zpětnou  transformaci  [Xs/Zs,Ys/Zs], pak  získáme  2D
       souřadnice právě vykreslovaného bodu v shadow bufferu. Je potřeba
       je  samozřejmě posunout do středu shadow bufferu, podobně jako 2D
       souřadnice bodu polygonu. Ty jednoduše lineárně interpolujeme při
       samotném  vykreslování textury a pomocí takto získaného odkazu do
       shadow   bufferu   velmi  jednoduše  zjistíme,  zda  index  právě
       vykreslovaného  polygonu koresponduje s indexem v shadow bufferu.
       Pokud ano, jsme ve světle, jinak je tam tma. Geniálně jednoduché,
       jednoduše geniální. Ale neradujme se předčasně!

            Pokud  jste pochopili podstatu, jistě vám neuniklo, že tento
       velmi  efektivní  algoritmus má svoje poměrně závažné mouchy. Asi
       nejmarkantnější  je problém s polygony, jejichž průmět do pohledu
       světla  je  velmi  úzký. Tento problém v analytické metodě zřejmě
       odpadá.  Vzhledem  k  tomu, že celý prostor vlastně kvantujeme do
       konečného  rastru,  musíme  se  smířit  s  tím,  že nastane jistý
       kvantizační  šum.  Ve  výsledku  se  celá  nedokonalost projevuje
       zubatými  přechody  mezi světlem a stínem v oblastech, které jsou
       od  světla  relativně  vzdálenější, nebo  v  případě,  kdy  se na
       polygonu  právě střídá světlo a stín. Vhodnou volbou cesty kamery
       můžeme  první  problém  před divákem šibalsky skrýt, ale střídání
       světla  a  stínu, které nastává právě při tenkém průměru polygonu
       do  shadow  bufferu,  je  třeba  řešit radikálním způsobem. Pokud
       oželíme  rychlost, je  možné  interpolovat  barvu, a částečně tak
       zubatost  vyhladit,  ale  lepší  cesta je zvolit jistou povolenou
       tloušťku  polygonu v shadow bufferu. Všechny polygonu, které jsou
       moc  tenké, označíme  jako neosvětlené, takže ve výsledku se daný
       polygonu  zatemní  skokem. V mnohých případech to divák prakticky
       nezaregistruje.  Pokud  se budete pozorně dívat třeba v Spotlite,
       zjistíte, že tento skok je jasně patrný ve scéně s kameny. Druhou
       kvantizační chybu si můžete prohlédnout ve Fulcrum, nebo je jasně
       patrná  na  tomto  obrázku . Shodou  okolností  se  jedná  také o
       šutráky,  tentokrát  levitující.  Sledujte pozorně stíny, až bude
       robot  procházet  chodbou s větrákem. Digisnap tento problém řeší
       vhodnou  volbou  cesty  kamery a světla, takže třeba ve Spotlite,
       kde  je celý  "akční"  prostor  velmi uzavřený, se s touto chybou
       nesetkáte.  Z  výše uvedených důvodů není tento systém vhodný pro
       realtime  enginy  3D  her, protože k podobným kvantizačním chybám
       může  obecně  docházet  velmi často. Pro statické 3D scény dem je
       však velmi užitečný a hlavně rychlý.

            Ovšem to není zdaleka konec možností této metody. Výborně ji
       lze  využít  například  k  emulaci  promítačky.  Prostě  k shadow
       bufferu  přidáme  ještě  nějakou  texturu  a  stejným postupem ji
       nanášíme  na  polygony.  Místo  zvyšování  intenzity  textury  ve
       světle, zamixujem RGB hodnoty shadow textury do textury polygonu.
       Vznikne  tak  obraz, který velmi realisticky simuluje promítačku.
       Lze  tak  také vytvářet plynulé přechody mezi světlem a stínem na
       hranicích  kuželu, když se místo shadow textury vygeneruje jakýsi
       light shadow buffer, který svým tvarem kopíruje inverzní kružnici
       kužele,  ale obsahuje informace o intenzitě daného bodu. Potíž je
       ovšem  v  tom,  že  plynulý  přechod  nastane  pouze na hranicích
       kužele, nikoliv obecně na hranici mezi světlem a stínem. To občas
       vypadá  dosti  nevěrohodně.  Například  ve  Fulcrum světlo krásně
       plynule  přechází  v  tmu, ale stín robota je ostrý a ještě velmi
       hrbolatý.  Lze  to  řešit,  pokud  použijeme  malý trik. Označíme
       jistou plochu (např. podlahu) jako polygon, na který budeme vrhat
       stíny.  Pokud  při  vykreslování  textur za pomoci shadow bufferu
       zjistíme,  že  se  nacházíme na přechodu mezi obecným polygonem a
       podlahou  v  takové  konstelaci,  že  právě  počítaný pixel je na
       podlaze,  spočítáme  kolik  okolních pixelů leží na této ploše. Z
       takto  získané  hodnoty  volíme  světlost  daného  bodu. Pokud by
       Digisnap použil tento trik, mohl by být přechod stínu robota méně
       kostrbatý. Ostatně modifikací tohoto principu označování polygonů
       lze  velmi  efektivně  eliminovat  některé přechodové kvantizační
       chyby  úzkých polygonů v shadow bufferu. Zakážeme-li, aby některé
       menší   objekty  na  sebe  navzájem  vrhaly  stíny,  výrazně  tak
       urychlíme  vykreslování  scény  bez  výrazné  újmy  na  výsledném
       vizuálním  dojmu.  Zdá  se,  že  Digisnap  tento způsob eliminace
       menších  polygonů  z  procesu stínování používá také. I zde lze s
       výhodou  využít  nesoustředění  lidského oka. Například stín ruky
       robota na vlastním těle není tak patrný jako stín jeho postavy na
       podlaze.  Je  ale  potřeba  toto  indexování  volit  chytře,  aby
       nedocházelo ke zřetelným chybám.

            Na  závěr  se  musím omluvit za poměrně odbornější charakter
       tohoto  článku,  který vyžaduje už jisté znalosti problematiky 3D
       enginů.  Je  tak  trochu  v  závěsu  za  Shakulovým seriálem o 3D
       enginech,  který  zatím  není  ještě  v takové fázi, aby člověku,
       který  je v  tomto obrou začátečníkem, poskytl informace k plnému
       pochopení   této   stínovací   metody.   Chtěl   jsem  prostě  co
       nejjednodušeji  a  ve zkratce nastínit celou metodu včetně jejích
       nedostatků  a  dalších  možných  aplikací. K její implementaci je
       zapotřebí  relativně  rozsáhlý kód, takže zde nebudu uvádět nějké
       příklady, které by byly samy o sobě vytržné z celkového kontextu.
       Pokud  by měl někdo hlubší zájem o implemntaci dynamických stínů,
       nechť  se  obrátí  na  mojí  adresu (redox@bbs.infima.cz) a já mu
       poskytnu konkrétní informace a rady, jak na to.

                                                            ReDox


            výheň