Pokročilé techniky 2004-2005 Josef Pelikán, MFF UK Praha http://cgg.ms.mff.cuni.cz/~pepca/ Josef.Pelikan@mff.cuni.cz
Obsah vylepšení osvětlovacího modelu dynamické mapy okolí ( environment maps ) generování a používání světelných map ( light maps, irradiance maps ), refrakce, bump-mapping, víceprůchodové algoritmy práce s buffery (stencil buffer, hloubkový buffer, akumulační buffer) výpočet vržených stínů shadow buffers, promítané stíny objemové stíny ( shadow volumes ) zobrazování CSG,
Normálové mapy ( bump-mapping ) bump-mapping modulace přesného normálového vektoru (z 3D modelu) napodobení drobných nerovností, hrbolatého povrchu, apod. data jsou obvykle uložena v obyčejné 2D textuře ( R 2 R 3, [ s, t ] [ N x, N y, N z ] ) normal map tečný prostor ( tangent space ): souřadné osy: tečna T, normála N, binormála B normálová textura obsahuje data z tečného prostoru [ N t, N n, N b ] (původní normála má směr [0,1,0])
Normálová mapa v tečném prostoru tangent space [T,N,B] dn N R world space T y B -z N = B T x
Tečný prostor pokud nepotřebujeme světové souřadnice, transformujeme všechny relevantní vektory do tečného prostoru (už ve vrcholech) směr ke kameře, ke světelným zdrojům ( half vector ),.. v tečném prostoru (v prostoru normálové textury) pak snadno spočítáme osvětlení nebo musíme zůstat ve světových souřadnicích: např. kvůli obrazu okolí ( environment map ) interpolace matice tangent world do fragmentů (problém s ortogonalitou!?) výsledek přečtený z normálové mapy se musí transformovat do světových souřadnic (dál jako obvykle)
Mapy okolí ( environment ) Environment map HW umí používat texturu uloženou jakou šest stěn krychle ( cube-mapping ) adresování 3D vektorem (nemusí být normalizovaný) statická i dynamická ( render target ) data nejčastější použití: dokonalý zrcadlový odraz ( environment map ) měkký odraz, difusní složka světla simulace reálného osvětlovacího modelu lom světla kombinace s bump-mapping
Cube-map textura technické využití: normalizace 3D vektoru,.. uložení libovolné výpočetně náročné funkce R 3 R 3 pro použití v shaderech (fragment shader, vertex shader od verze 3 NV40) environment mapping : pozor na souřadnou soustavu 3D vektoru obvykle se používají světové souřadnice navíc transformační matice model svět (nebo méně často clip svět ) pro odraz nebo lom světla existují v Cg knihovní fce
Souřadné systémy [ s, 1.0, t ] s = x/y, t = z/y N R T world space N = B T y -z B tangent space [T,N,B] x
Dokonalejší osvětlení difusní složka: cube-map se adresuje normálovým vektorem N dopředu spočítaný součet světla z okolí (integrál) pomocí faktoru cos α lesklá složka ( specular ): přesně umíme reprezentovat jen modely, kde se jako kvalitativní člen vyskytuje cos β cube-map se adresuje odraženým vektorem R ( reflect() v Cg) dopředu spočítané rozmazání okolí faktorem cos h β
Světelné směrové mapy world space cos α N R cos h β y -z cos e γ T x
Lom světla zjednodušený přístup: cube-map se adresuje vektorem T obvykle se používá dokonalý (nerozmazaný) obraz okolí (zřídka okolí rozmazané faktorem cos h β ) možnost simulovat rozklad světla různé indexy lomu pro jednotlivé barevné složky společný obraz okolí teoretická možnost započítat do refraction map i druhý lom při výstupu paprsku z tělesa
Víceprůchodové algoritmy 3D scéna (nebo její části) se prochází několikrát jiné nastavení GPU (použití různých bufferů, depthtest, stencil-test, apod.) jiné transformační matice, projekce jiné shadery předávání dat mezi jednotlivými průchody: buffery GPU (frame buffer, depth-buffer, stencil buffer, accumulation buffer) textury (shadow map, environment map,...)
Akumulační buffer, obraz okolí použití akumulačního bufferu: anti-aliasing rozmazání pohybem ( motion blur ) hloubka ostrosti objektivu ( depth of field ) opakovaný průchod scénou s odlišným nastavením transformační (projekční) matice dynamický výpočet obrazu okolí ( environment ): chceme, aby se animace odrážela v lesklých objektech pro cube-map musíme 3D scénu nakreslit 6 nebo to nějak ošidit při znalosti animace
Odraz v rovinném zrcadle jeden průchod navíc pro každé rovinné zrcadlo pokud nechceme vícenásobné odrazy 1. kreslení normální scény zrcadlo se nekreslí (nebo jenom lehce svojí barvou) zrcadlo zapisuje do šablony (pokud je zrcadel víc, každé má svoji speciální hodnotu), ostatní šablonu nulují k+1. kreslení scény odražené v k-tém zrcadle část scény před zrcadlem se kreslí s modifikovanou transformační maticí (příp. nastavím alpha-blending ) je dovoleno kreslit jen přes nastavenou šablonu depth-test je povolen (depth-buffer inicializuji)
Jedno rovinné zrcadlo 1. 2. stencil = 1
Vržené stíny několik různých přístupů ostré hranice stínů ( jeden průchod algoritmem ) měkké stíny (více průchodů, kombinace několika výsledků..) stíny vrhané do jediné roviny postup je jednoduchý, ale nepraktický shadow mapping stínový depth-buffer, podpora HW objemové stíny (stínová tělesa, shadow volumes ) nejpřesnější přístup, výpočetně náročné
Stíny promítnuté do roviny výpočet stínů, které vznikají při osvětlení scény ostrým zdrojem světla příklad použití šablony ( stencil ) a více průchodů scénou šablona maskuje plochy, na které má stín dopadat, zařídí, aby se stíny neduplikovaly jednoduchý algoritmus: stíny se vrhají na jedinou rovinu ( rovina příjemce ) obraz stínu může být neprůhledný (ve stínu zaniká původní barva/textura příjemce) nebo průhledný (stín jen snižuje množství světla)
Stíny vrhané do roviny jednoduchý algoritmus, stíny se vrhají do jediné roviny projekční matice z 3D světa do roviny příjemce vrhači stínů šablona příjemce stínu (stencil = 1) plochá stínová tělesa
Stíny vrhané do roviny postup vykreslování: 1. celá 3D scéna se vykreslí v běžném promítání příjemce nastavuje daný bit šablony všechny ostatní plochy tento bit nulují 2. s vypnutým testem hloubky se všichni potenciální vrhači stínu promítnou do roviny příjemce musí se nastavit speciální promítací matice stínové plošky se kreslí pouze na místa, kde je nastaven daný bit šablony (z prvního průchodu) používají-li se poloprůhledné stínové plošky, je nežádoucí, aby se dvě překreslily přes sebe i tady pomůže šablona (první stínová ploška ji zpátky vynuluje)
Shadow mapping 1. scéna se nakreslí z pohledu světelného zdroje: není potřeba zapisovat do frame-bufferu, jen do depthbufferu 2. depth-buffer se přesune do textury ( shadow map ) scéna se nakreslí v běžném promítání podle kamery používají se projektivní texturové souřadnice GPU umí testovat skutečnou vzdálenost fragmentu od zdroje světla (světové souřadnice) proti hodnotě v hloubkové textuře: float4 shadow = tex2dproj( shadowmap, texcoordproj );
Stínový depth-buffer ( shadow map ) z z = d A A shadow buffer B z = d B < d A (stín)
Stínová tělesa ( volume shadows ) každé osvětlené těleso vrhá nekonečný stín (množina zastíněných bodů = stínové těleso ) boční stěny stínového tělesa uvažujeme jako neviditelné, virtuální čtyřúhelníky paprsek od kamery k zobrazovanému tělesu se proti takovým stěnám testuje GPU může virtuální stěny rasterizovat a kreslit je do šablony (obraz při tom zůstává nezměněn) nakonec buffer šablony vyznačuje osvětlenou nebo zastíněnou část scény tento postup se musí opakovat pro každý světelný zdroj
Stínové objemy I společný první krok kreslení celé skutečné scény zapisuje se do depth-bufferu, osvětlení: ambient boční stěny stínového tělesa se dělí na přivrácené a odvrácené při kreslení stínových těles se nezapisuje do depthbufferu (ale k testování viditelnosti se používá) druhý krok kreslí pouze boční stěny stínových těles: přivrácené viditelné stěny inkrementují šablonu odvrácené viditelné stěny dekrementují šablonu třetí krok má v šabloně nulovou hodnotu pro osvětlené části (přidá se příspěvek světelného zdroje)
Stínové objemy I 0-1 1 1 +1 0 světlo +1 +1 1 1 stín +1 0
Stínové objemy I - selhání -1 0 0-1 0 světlo 1 stín
Stínové objemy II kamera může být umístěna kdekoli stínová tělesa jsou dokonale uzavřená čepičkami : jedna je tvořena osvětlenou částí tělesa, druhá leží v nekonečnu druhý krok kreslí boční stěny stínových těles a obě čepičky : přivrácené neviditelné stěny dekrementují šablonu odvrácené neviditelné stěny inkrementují šablonu třetí krok má v šabloně nulovou hodnotu pro osvětlené části (přidá se příspěvek světelného zdroje)
Stínové objemy II +1 +1 +1 0 1 1 0 světlo 1 1 stín 0-1 +1
Stínové objemy II v pořádku 0 +1 +1 1 1 0 světlo 1 stín
Vrcholy v nekonečnu boční stěny i zadní čepička potřebují mít vrcholy v nekonečnu vzdálenější od kamery než jakékoli jiné objekty průmět vrcholu [ x, y, z, 1 ] do nekonečna: [ x, y, z, 0 ] projekční matice s hodnotou far = : A = 2n r l B = r l r l [ M n,,r, l, t, b = A C = 2n t b 0 0 0 ] 0 C 0 0 B D 1 1 0 0 2n 0 D = t b t b
Průmět do nekonečna projekce vlastního bodu (včetně vydělení homogenní složkou): [ x, y, z, 1 ] M = [ x z A B, y z C D, 1 2n z ] projekce nevlastního bodu: [ x, y, z, 0 ] M = [ x z A B, y z C D, 1 ]
Přivrácené / odvrácené stěny z hlediska pozorovatele umí filtrovat GPU ( face culling ) podle orientace vrcholů stěny v NDS: glenable( GL_CULL_FACE ); glfrontface( GL_CCW ); glcullface( GL_BACK ); // draw front faces only z hlediska světelného zdroje výpočet na CPU (podle normálových vektorů v prostoru) možnost použití programovatelných GPU (vertex pr.) eliminace nesprávných primitivů degenerací nevýhoda: mnohem větší počet vrcholů posílaný do GPU
Eliminace stěn ve vertex procesoru nelze vrcholy ani primitiva zrušit (na běžných GPU) mohu nechat nepatřičné stěny ořezat nebo zdegenerovat s každým vrcholem stěny se musí z CPU posílat data potřebná k rozhodování zde např.: normálový vektor plošky nemá-li být stěna nakreslena, všechny její vrcholy jsou ve vertex shaderu nastaveny např. na [ 2, 0, 0, 1 ] tj. mimo frustum (uplatní se ořezávání GPU), kromě toho by v rasterizéru nevzniknul ani jeden fragment sousední stěny nesmějí sdílet vrcholy (mnoho dat)!
Eliminace stěn - ukázka V 4 Data (normály posílám jako TEXCOORD0): N 2 V 1 N 3 V 5 gltexcoord3fv( N1 ); glvertex4fv( V1 ); glvertex4fv( V2 ); glvertex4fv( V3 ); přivrácené odvrácené V 3 N 1 V 2 gltexcoord3fv( N2 ); glvertex4fv( V1 ); glvertex4fv( V3 ); glvertex4fv( V4 ); gltexcoord3fv( N3 ); glvertex4fv( V1 ); glvertex4fv( V5 ); glvertex4fv( V2 );
Plášť stínového tělesa tvoří ho nekonečné čtyřúhelníky promítané z obrysových hran stínícího tělesa obrysové vzhledem ke světelnému zdroji jedna sousední stěna je přivrácená, druhá není je-li hrana [ x 1, y 1, z 1, 1 ] [ x 2, y 2, z 2, 1 ] obrysová, vrhne nekonečný stínový čtyřúhelník: [ x 1, y 1, z 1, 1 ], [ x 2, y 2, z 2, 1 ], [ x 2, y 2, z 2, 0 ], [ x 1, y 1, z 1, 0 ] rozhodování o obrysových hranách stínu na GPU každá hrana má své vlastní 4 vrcholy (plýtvání..) s každým vrcholem se posílají normály dvou sousedních stěn tělesa (míří-li jedna ke zdroji a druhá od něj obrys)
Plášť stínového tělesa - ukázka N 1 V 1 N 3 V 2 V 2 i V 1 i Data (normály posílám jako TEXCOORD0 a TEXCOORD1): glmultitexcoord3fv( GL_TEXTURE0, N1 ); glmultitexcoord3fv( GL_TEXTURE1, N3 ); glvertex4fv( V1 ); glvertex4fv( V1i ); glvertex4fv( V2i ); glvertex4fv( V2 ); glmultitexcoord3fv( GL_TEXTURE0, N4 ); glvertex4fv( V2 ); glvertex4fv( V2i );
Statické měkké stíny (intervaly zákrytu) speciální stínovací metoda pro statickou scénu a zdroj světla pohybující se po pevné křivce např.: statický exteriér a Slunce předem spočítané intervaly zákrytu pro každý bod ve scéně! charakteristická funkce osvětlení (jako funkce času) časově náročný výpočet (Ray-tracing až hodiny CPU) uchování výsledků ve speciálních stínových mapách (vektorová reprezentace: začátky a konce světelnýh intervalů) interpolace měkkých stínů v reálném čase na GPU
Intervaly světla a stínu světelný zdroj se pohybuje v čase po pevné křivce: t 2 t 3 t 4 t 5 t 1 t 6 P t 1 t 2 t 3 t 4 t 5 t 6 t
Rozmazání funkce viditelnosti pro měkké okraje stínu se funkce viditelnosti rozmaže (fragment shader na GPU): t 1 t 2 t 3 t 4 t 5 t 6 dt t t 1 t 2 t 3 t 4 t 5 t 6 t
Efektivní výpočet interpolace zvlášť reprezentuji začátky ( R i - rise ) a konce ( F i - fall ) osvětlených intervalů: R 1 F 1 R 2 F 2 R 3 F 3 t V lin t = 1 0 V point u W dt t u du n 1 V lin t = i=1 dt max 1 0, min t 2 dt, F i max t 1 2 dt, R i
Fragment shader pro interpolaci R i a F i se předávají dvěma texturami (je tam místo pro 4 světelné intervaly), t-dt/2, t+dt/2 a 1/dt pomocí uniform parametrů: half softshadow ( sampler2d risetex, sampler2d falltex, float2 texcoord, half intstart, half intend, half intinvwidth ) // t-dt/2 // t+dt/2 // 1/dt { half4 rise = h4tex2d( risetex, texcoord ); half4 fall = h4tex2d( falltex, texcoord ); half4 mint = min( fall, intend ); half4 maxt = max( rise, intstart ); return dot( intinvwidth, saturate( mint maxt ) ); }
Zobrazování CSG scén na GPU převod elementárních těles na mnohostěny vyhodnocování množinových operací na GPU: sjednocení je triviální (nakreslí se vše přes sebe, s pomocí viditelnosti depth buffer ) průnik a rozdíl: pomocí šablony ( stencil ), rozlišuji přivrácené a odvrácené stěny 1989: Goldfeather et al. normalizace CSG stromu rozklad na sjednocení součinů (součin obsahuje průniky a rozdíly) implementace potřebuje několik depth-bufferů a šablonu (+ nutnost kopírovat mezi sebou depth-buffery)
Sekvenční konvexní odečítání 2000: Stewart et al. Sequenced Convex Subtraction ( SCS ) nepotřebuje kopírování depth-bufferů, složité depth-testy jednotlivá elementární tělesa musí být konvexní O(n) průnik n konvexních těles O(n 2 ) rozdíl n konvexních těles (O(kn) s omez. zákr.) tři fáze algoritmu: 1. předzpracování (normalizace CSG, setřídění odečítacích sekvencí zepředu dozadu) 2. zpracování depth-bufferu (pro každý součin + merge) 3. finální vykreslení výsledku do frame-bufferu
Průnik n těles inicializace: depth = near; stencil = 0; průchod přivrácenými stěnami jednotlivých těles if ( front > depth ) depth = front; průchod odvrácenými stěnami (počítání zákrytů) if ( back > depth ) stencil++; odstranění pixelů s menším počtem překrytých objektů než n if ( stencil!= n ) { stencil = 0; depth = far; }
Průnik - ukázka S 1 S 2 S 3 2 3 3 2
Odečítací sekvence určení správné posloupnosti odečítání těles: odečítat se musí zepředu dozadu např. X A B se nahradí univerzální X A B A A,B,A je korektní univerzální odečítací posloupnost viz posloupnosti obsahující všechny zákryt. permutace odečítání odpředu: průchod všemi odečítanými tělesy if ( front < depth ) stencil = 1; else stencil = 0; pro každé těleso se hned projde i odvrácená část if ( back > depth && stencil == 1 ) depth = back;
Odečítání - ukázka X - S 1 - S 2 - S 1 S 1 X S 2 I I II II III III
Odečítání - výsledek X - S1 - S 2 - S 1 S 1 X S 2!
Úplně odečtené části odstranění částí společného průniku, které byly odečtením zcela eliminovány: inicializace: stencil = 0; průchod všemi tělesy z průniku (jen jejich zadní stěny nalezení prázdného výsledku) if ( back < depth ) stencil = 1; eliminace úplně odečtených partií if ( stencil == 1 ) depth = far; stencil = 0;
Slévání součinů a vykreslení výsledku výsledek výpočtu součinu = jeho depth buffer slévání výsledku jednoho součinu (tj. sjednocení) if ( depth < depth total ) depth total = depth; finální vykreslení výsledku jiná logika pro pronikaná a jiná pro odečítaná tělesa pronikané těleso (pixel po pixelu): if ( front == depth total ) draw(front); odečítané těleso (pixel po pixelu): if ( back == depth total ) draw(back);
Literatura Tomas Akenine-Möller, Eric Haines: Real-time rendering, 2 nd edition, A K Peters, 2002, ISBN: 1568811829 Randima Fernando, Mark J. Kilgard: The Cg Tutorial, Addison-Wesley, 2003, ISBN: 0321194969 OpenGL ARB: OpenGL Programming Guide, 4 th edition, Addison-Wesley, 2004, ISBN: 0321173481 ed. Randima Fernando: GPU Gems, Addison- Wesley, 2004, ISBN: 0321228324