Aleš Borek ALTAR interactive Lightmapy Aleš Borek ALTAR interactive
Obsah Úvod Mapování texturových souřadnic Rozložení do textury Softwarové osvětlení Hardwarové po jednotlivých polygonech Hardwarové přes shadowmapy Techniky na zlepšení kvality
Co to je? Stará technika na zlepšení vizuálního dojmu Druhá textura – barva světla, jak svítí na daný bod Rychlé, dobrý poměr cena/výkon Nemá smysl kreslit přímo do hlavní textury Není nutno použít fixní pipeline Bodové osvětlení / radiosita
S lightmapou a bez ní
Vytváření Dvě nezávislé fáze Namapování texturových koordinát a umístění v textuře Vlastní olightmapování (přiřazení barvy jednotlivým texelům)
Namapování souřadnic – od grafiků Teoreticky možno použít namapování od grafiků pro první texturu Možné problémy se spojováním a scalingem Může se ale lépe využít textura a může to vypadat lépe Propagace chyb od grafiků
Namapování souřadnic Rozdělení geometrie do šesti skupin podle rovin x, -x, y, -y, z, -z Mapování pak není úplně přesné (koule) Problém u terénu Lightmapa se na tyto trojúhelníky bude nanášet, jako bychom se na ně dívali z příslušné roviny
Přiřazení souřadnic Spojení sousedních trojúhelníků z jedné skupiny do grupy Vyhledávání po hranách Grupa zabere vždy obdélníkový prostor Snadno spočítáme výšku a šířku v textuře, ale je problém tam tu grupu zařadit
Zařazení grupy do textury NP-úplný problém: jak ideálně zaplnit texturu? My používáme stromovou strukturu Poté někdy off-line algoritmus na obecnou optimalizaci textur Další problémy Prázdný prostředek Šikmé trojúhelníky
Vlastní ligtmapování Softwarově přes kolizní strukturu Po trojúhelnících pomocí karty (nejsou nutné shadery) Použitelné pro radiositu Pomocí shadowmap přes shadery
Softwarové lightmapování V UFO: Aftermath Pro každý texel vedu přímku ke zdroji světla – když narazí – jsem ve stínu; jinak ve světle Světlo: barva * normála.DotProduct(lv) Kolize přes BSP-stromy Zrychlení: po trojúhelnících; průnik čtyřstěnu s geometrií; poté s jednotlivými trojúhelníky
Softwarové lightmapování Výhody Velké rozlišení možné při výpočtu (ne omezení 2048x2048) Lepší kontrola Nevýhody rychlost
Aplikace lightmapy Barva textury * barva lightmapy * 2 Tímto jsme schopni dosáhnout určitého přesvícení Možné samozřejmě i jiné aplikace (např. přičítání) I bez shaderů
Lightmapování pomocí karty Pokud se na trojúhelník, který osvětlujeme, díváme z pohledu světla a okolní geometrii vykreslíme černě, dostaneme přesně to, co chceme Pokud do vertexů dáme správné barvy (a příslušně vykreslujeme), není nutné používat shadery Textury pro každé světlo spojíme buď screen metodou (a+b*(1-a)) nebo přičtením
Trojúhelníky – bodové světlo Pro bodové světlo: nastavení pohledového jehlanu Obrácená normála trojúhelníku je směr pohledu – aby měl hloubku konstantní, jinak by to nevyšlo přesně: glLookAt(pozice, pozice-normála, up_vektor); glFrustum(minx*tz, maxx*tz, minz*tz, maxy*tz, nearz, maxz+1.0f); kde tz = nearz/maxz a min*, max* jsou minima a maxima 3d-souřadnic po pronásobení kamerou a nearz = 1.0f
Trojúhelníky – bodové světlo Obr.: světlo osvětlovaný trojúhelník textura
Trojúhelníky – paralelní světlo Pro paralelní světlo: nastavení pohledového jehlanu Směr opět obrácená normála – ne směr světla, projekční matici ale nutno odvodit (sx = minx – bod.x, sy = miny – bod.y) projection_matrix.a00 = 2/(maxx-minx); projection_matrix.a01 = 0; projection_matrix.a02 = 2*(minx-sx)/(maxz*(maxx-minx)); projection_matrix.a03 = -2*sx/(maxx-minx)-1; projection_matrix.a10 = 0; projection_matrix.a11 = 2/(maxy-miny); projection_matrix.a12 = 2*(miny-sy)/(maxz*(maxy-miny)); projection_matrix.a13 = -2*sy/(maxy-miny)-1; projection_matrix.a20 = 0; projection_matrix.a21 = 0; projection_matrix.a22 = -2/maxz; projection_matrix.a23 = -1;
Kreslení trojúhelníků - výhody Při lightmapování se využije rychlá grafická karta Možno použít i barevná skla (nekreslila by se černě, ale v příslušné barvě, nutno mít setříděnou geometrii) Použitelné i pro radiositu Není nutné použít shadery
Kreslení trojúhelníků - nevýhody Nutno pracovat po trojúhelnících a ne po celých skupinách (jen když mají stejnou normálu) Při velkých grupách opět omezení velikostí pamětí na kartě Rychlost není ještě ideální
Shadowmapy – paralelní světlo Používá technologii shadowmap (pro dynamické zobrazení stínů) při počítání lightmap Scéna se vykreslí v ortogonálním zobrazení ve směru světla do shadowmapy – zapisuje se jenom z-buffer (v přesnosti 24 nebo 32 bitů) Při kreslení texelu se spočítá jeho z-hodnota v původním ortogonálním zobrazení a porovná se s tou spočítanou – pokud je větší, je bod ve stínu
Shadowmapy – paralelní světlo
Shadowmapy – lightmapování Pro každou grupu se nastaví modelview matice podle roviny, do které byla grupa promítnuta, projekční matice zůstává identita Musíme nastavit jen správně shadery, aby se při renderování grupy brala do úvahy shadowmapa pro dané světlo
Shadowmapy – bodové světlo Nestačí jedna shadowmapa, protože bodové světlo obsáhne celý prostor Možnosti Sférické – jen jedna shadowmapa, velice nepřesné Cube-mapa – šest shadowmap Dual-parabolické – dvě shadowmapy; používáme
Shadowmapy – dual-parabolické Používají se dva paraboloidy (f(x,y) = ½ - ½(x2 + y2)), které zachytí celou scénu
Shadowmapy – dual-parabolické
Dual-parabolické - implementace uniform vec3 lightPos; uniform float sign; uniform float maxDepth; void main(void) { vec4 transfP = gl_ModelViewMatrix * gl_Vertex; vec3 P = lightPos - transfP.xyz; P.z *= sign; if (P.z <= -0.5) P.z = -maxDepth-1.0; P = normalize(P); P.z += 1.0; vec4 OutPos; OutPos.x = P.x / P.z; OutPos.y = P.y / P.z; OutPos.z = (length(P.xyz) - gl_DepthRange.near) / gl_DepthRange.diff + 20.0; OutPos.z = (OutPos.z / maxDepth) * 2.0 - 1.0; OutPos.w = 1.0; gl_Position = OutPos; }
Dual-parabolické - problémy Tam, kde nastává řez (v našem případě rovina z), na sebe nemusí navazovat trojúhelníky: nutno zde rozříznout geometrii Geometrie musí být hodně teselovaná: v našem případě není problém Není to moc přesné
Dual-parabolické - problémy
Filtrování textur Díky filtrování textur (aby byly rozmazanější) je nutné zabarvit černé pixely u okraje textury grupy Pokud nebylo do bodu textury zapsáno (zjištěno pomocí alphy), uděláme průměr z okolních bodů a bod vyplníme Možno dělat jako filtrování shaderem, v našem případě ještě přidáme ambientní složku
Filtrování textur - obrázek
Paralelní světlo - implementace Vertex shader varying vec3 normal; uniform mat4 shadowCam; void main(void) { gl_Position = ftransform(); normal = gl_Normal; gl_TexCoord[0] = gl_ModelViewMatrix * gl_Vertex * shadowCam; } Fragment shader uniform vec4 lightColor; uniform vec3 lightDir; varying vec3 normal; uniform sampler2DShadow shadowMapTex; vec3 L = -normalize(lightDir); vec3 N = normalize(normal); float shadowTerm = shadow2DProj(shadowMapTex, gl_TexCoord[0]).r; gl_FragColor.rgb = lightColor.rgb * max(dot(N, L), 0.0) * shadowTerm; gl_FragColor.a = 1.0;
Paralelní světlo - implementace shadowCam bias matice (převádí z [-1, 1] na [0, 1]) * projekční matice kamery světla renderovaného do shadowmapy * pohledová matice kamery světla renderovaného do shadowmapy * inverzní pohledová matice současné kamery
Bodové světlo - implementace uniform sampler2DShadow shadowMapTex2; uniform sampler2DShadow shadowMapTex; uniform float endRad; uniform float fullRad; uniform vec4 lightColor; varying vec3 normal; varying vec3 Pos; void main(void) { float k; float lvLen = length(Pos); vec3 L = normalize(Pos); vec3 N = normalize(normal); float attenuation = 1.0 - min((max(lvLen - fullRad, 0.0))/(endRad - fullRad), 1.0); float Alpha = ((Pos.z / endRad) >= 0.0)? 1.0: -1.0; float ZValue = (lvLen - gl_DepthRange.near) / gl_DepthRange.diff; vec3 tPos = L;
Bodové světlo - implementace tPos += vec3(0.0, 0.0, Alpha); tPos.x /= (tPos.z * -Alpha); tPos.y /= (tPos.z * -Alpha); tPos.xy = vec2(0.5, 0.5) + 0.5 * tPos.xy; tPos.z = ZValue/endRad; if (Alpha >= 0.0) { k = shadow2D(shadowMapTex2, tPos).r; } else { k = shadow2D(shadowMapTex, tPos).r; gl_FragColor.rgb = lightColor.rgb * attenuation * max(-dot(N, L), 0.0) * k; gl_FragColor.a = 1.0;
Shadowmapy - výhody a nevýhody Relativní rychlost – pro jedno světlo „konstantní“ čas – dokonce ani tolik nezáleží na tom, jestli světlo vrhá stín nebo ne Omezení velikostí texturové paměti (hlavně u shadowmapy) – menší kvalita Nemožnost barevných skel Rozdíly u ATI a n-Vidie
Supersampling Použitelné jen u softwarových lightmap díky paměťovým omezením Jeden texel v lightmapě odpovídá 4, 9, 16, … texelům při výpočtu – je jejich průměrem
Multisampling Pro každé světlo se lightmapa vypočítá vícekrát – světlo je trochu vždy trošku posunuté Lépe řeší světlo těsně u stěny Opět zvýší kvalitu bez nutnosti mít velkou texturovací paměť Ještě jsme neimplemetovali
Perlin-noise Lightmapa se dá využít i pro šum – zbytečné plýtvat další texturou Pomáhá rozbít repetitivnost u velkých ploch, kde se opakuje textura, protože lightmapa se neopakuje
Aplikace lightmap Texel v lightmapě nemusí kódovat pouze barvu Jednou z možností je do barevné složky (R, G, B) zakódovat intenzitu světla – a při aplikaci pixel-shaderem vynásobit se skutečnou barvou světla: možno udělat například rozžínající se a plápolající oheň
Aplikace lightmap - příklad
On-line přepočítávání lightmap Nutno mít přesně označeno, které grupy a která světla se musí přepočítat – většinou se to předpočítává Použitelné i u softwarových lightmap – UFO: Aftermath Pro UFO: Aftershock ještě nevíme
Reference GameDeveloper – August 2004 pg.22-28; Brian Ramage: Fast Radiosity using Pixel Shaders Brabec, Annen, Seidel: Shadow Mapping for Hemispherical and Omnidirectional Light Sources