English Welcome Kecy Programování 3D - Engine Guestbook Odkazy Downloady O autorovi Napiš mi Mailform |
3D - Engine 08 - mip-mapping a filtrování textur
Ahoj ! Tak tady máme prázdniny a taky nový díl seriálu o enginech. Doufám, že vás minule
nezmátly ty grafy na konci. To první číslo je délka segmentu (podle toho je to seřazené)
a to číslo v závorce je jejich počet. Sloupec je potom jejich relativní počet. Kód
na generování byl ve zdrojáku a spouštěl se klávesou "Home", můžete si to zkusit, pokud
jste si toho nevšimli. Minule jsem sliboval něco o Mip-Mapách, tak jdeme na to !
Co to je Mip-Mapping ?
To byly obrázky z minulého dílu. Oba mají jedno společné : obsahují plochy, kde detail
textury převyšuje detail obrazovky. Na tom prvním je to třeba podlaha, ale i zdi, i když
to na nich není tolik vidět a na druhém to jsou "větráky" (ty díry ve stropě). Když je
to jako obrázek, ještě se to dá přežít, ale když se ve světě hýbete, obraz všelijak šumí
a přeblikává. To řeší právě Mip-Mapy. Podívejte se na screenshot z Quaka I :
Nalevo se podívejte na ten nápis "Exit" a porovnejte ho s tím napravo : nalevo je hráč
od té stěny trochu dál a textura má menší detail. Potom přijde blíž a textura se přepne
na její verzi s vyšším rozlišením. Tím se odstraní šum na stěnách, vzdálených polygonech
s "proužkatou" (kontrastní) texturou, kde je moc detailu na moc malou plochu. Ještě vám
sem dám dva obrázky (snad to bude prohlížitelné, na 56k modemu asi jo :-) ) Jsou z mé
hry, která se tady taky za chvíli vyskytne. Na tom horním jsou vypnuté Mip-Mapy :
(všimněte si zdi za světlem, chodeb vedoucích dozadu... )
Mip-Mapy v praxi Mip-Mapping má dva klony : Ordinary Mip-Mapping (Quake, většina gfx karet včetně GeForce), nebo Bi-Directional Mip-Mapping. Potom je ještě rozdělení podle toho na co se mipmapa aplikuje, a to Mip-Mapping per poly a per scanline. My si ukážeme per poly, to znamená, že na celý polygon je použitá jedna mipmapa (Quake |, ][). Mip-Mapping per scanline používá třeba Unreal Tournament, Morrowind a Quake ]|[ Arena (maybe Doom ]||[). Opravdu jde jen velmi těžko rozeznat (v Unrealu to opravdu najít skoro nešlo) :
Při používání per poly se doporučuje přerozdělovat velké polygony na menší části. Představte
si situaci, kdy jsou dva pokoje vedle sebe, podlaha jednoho jsou dva polygony, podlaha
toho druhého taky. Pro první pokoj pravděpodobně vyjde textura v plném detailu, pro druhý
třeba o dva stupně zmenšenější a na rozhraní podlah vznikne nehezký skok. Kdyby se poldahy
přerozdělily na víc polygonů, budou skoky menší. Přerozdělení můžete vidět třeba ve Quake ][
ve velkých místnostech :
Rozdělení větších polygonů na dílce (Quake ][)
Ordinary Mip-Mapping
Znamená to, že se mip-mapy vygenerují jako zmenšené verze textury s konstantním poměrem velikosti stran. Tudíž, když máme texturu velikosti 64×64, budeme mít mip-mapy 32×32, 16×16, 8×8, 4×4, 2×2 a 1×1. Mezi těmi se potom bude přepínat. Celé to zabere 1 + 1/2 + 1/4 + 1/8 + 1/16 + 1/32 .. = 2 krát více paměti, než jen klasická textura bez mip-map. (Textury v našem engine smějí mít velikost jen mocnin dvou, tudíž se dělí vždycky na poloviny. To ale není výsada jen našeho engine, hodně grafických karet taky nepodporuje jiné velikosti, teď nevím jak to přesně je, je možné že dosud ne a texturu upraví -zvětší- rozhraní pro práci s grafickou kartou - OpenGL, Direct 3D) Bi-Directional Mip-Mapping Bi-directional mip-mapping má mip-mapy zmenšené vždy po x-ové, y-ové a obou osách. Vypadá to takhle :
V levém horním rohu je originální textura (128×128), a ta se potom zmenšuje. Jak asi vidíte,
zabere to 4× víc paměti, ale výsledek není zase o tak moc lepší, takže se od tohoto nápadu
všeobecně upustilo. My si to ale napíšeme, zčásti proto že ukládat mip-mapy takhle je docela
dobrý nápad.
Jak generovat Mip-Mapy ? Mip-Mapy by měly být generovány buď dobrým grafickým programem, nebo nějakou téměř dokonalou rutinou. Určitě ne jen obyčejným převzorkováním, protože pak výsledek vypadá hrozně. Napsal jsem pro vás program, který Mip-Mapy vygeneruje. Volá se z příkazové řádky a podporuje bitmapy, PCX-ka, Targy a GIFy. Výstup vypadá stejně jako obrázek v sekci Bi-Directional Mip-Mapping, je možnost generovat i ordinární Mip-Mapy. Dělá to jednoduchý průměr RGB. Větší problém, než mip-mapu vygenerovat je při renderování vybrat, se kterou se bude kreslit. Jak vybrat tu správnou Mip-Mapu ? Mip-Level - stupeň zmenšení - se určuje podle velikosti texelu na obrazovce. Když texel (tj. bod textury namapované na nějakou face) zabere více, nebo jeden pixel, nic se neděje, ale když se zmenší natolik, že už nezabere ani jediný pixel, náš milý rasterizer určí, že se použije třeba každý čtvrtý pixel - při dalším snímku zase jiný, výsledkem čehož je šum, kterého se chceme zbavit, takže je čas nasadit mip-mapu o level dál, takže texely zase zaberou více místa, protože mip-mapa bude mít menší (poloviční) rozlišení. Pří této operaci však v jednom snímku bude mít textura velké rozlišení a v dalším poloviční, tudíž "blikne". Tento problém sice řeší trilineární filtrování, které určí, že z mip-mapy 1 se použije 20% a z mip-mapy 2 zbylých 80%, takže mip-mapy nepřeblikávají, ale postupně se prolínají. Tenhle způsob je v software modu takřka nezvládnutelný, takže ho používat nebudeme (ono stejně přepínání probíhá většinou ve větší vzdálenosti od kamery, takže není vidět) ale v example možná bude... No a teď si řekneme něco o velikosti texelu. Když mapujeme texturu, počítáme si "delty" souřadnic (dudx, dvdx...) ze kterých jde velikost texelu spočítat podle velice jednoduché rovnice : sqrt(dudx^2 + dudy^2) * sqrt(dvdx^2 + dvdy^2) Podle tohohle vzorce se spočítá plocha (v pixelech), kterou pokrývá texel. Má to ale jeden háček - delty, které spočítáme my jsou perspektivně korektní - vynásobené 1/z, ale to se pro tenhle vzorec nehodí, takže nezbývá než buďto znovu spočítat další, nebo se na to podívat z trochu jiného úhlu. Další jednoduchý algoritmus je vzít polygon a spočítat jeho povrch na obrazovce a "na textuře" - podílem potom dostaneme přibližně stejné číslo (nějakou tu nepřesnost zapříčiní rasterizer zokrouhlováním) V C-čku by to vypadalo nějak takhle :
Co to dělá ? V první části si to připraví pár proměnných, potom následuje smyčka, počítající povrchy polygonu na textuře a obrazovce a nakonec se spočítá mip_map_factor - poměr obou ploch. Druhá funkce podle "by watchko" určených konstant vrátí, kterou mipmapu by bylo dobré použít. (ono až zase tak od oka - jedno odpoledne strávené poletováním s kamerou nad potexturovaným obdélníkem a dolaďování) Example #1 Tak tady jsou MipMapy vypnuté Tady se používá ordinary MipMapping A tady zase Bi-Directional ...
Tak, můžu si být na 100% jistý že až tohle bude někdo číst, že mě kvůli těm obrázkům zabije
:-).. (snad jich nebude moc) Example je S-Buffer Engine z minulého čísla, stáhnout ho můžete
tady. Tak, co je nového ? Je tam vylepšený "Font Engine", který teď umí zobrazit písmo
transparentně (na obrázku) ve stylu á-la Half-Life :-). Potom je tam ta hlavní část: mipmapy
v souboru Raster.cpp přibylo na začátku pár řádek :
To je switch pro mipmapping - ke kterému řádku dáte jedničku, ten styl se použije. Potom je tam několik funkcí pro zjišťování velikosti texelu, z nichž jednu už jsem popsal před chvílí a další je tady :
Tahle varianta počítá plochu texelu právě z "delt" je to dost podobné funkci pro počítání delt pro texturování, ale chybí tu perspektivní korekce, která je tady nežádoucí. Jinak je to asi stejné. Samotná funkce pro výpočet texturovacích delt doznala trochu optimalizací, ušetřilo se několik dělení a unárních "-", to snad pochopíte z komentářů.. Textury jsou ve stejném formátu jako vyplivne Mip_Map "Creator", asi by bylo trochu ekonomičtější je počítat přímo při startu.. Tak, to by bylo k mipmapám a my se můžeme pustit do filtrování. Filtrování textur Až doteď naše engine nepoužívaly žádné filtrování, ale když teď snižujeme detail textur, začíná se projevovat že je všechno "zubaté". To řeší bilineární filtrování : Bilineární filtrování Při klasickém texturování, jak jsme to dělali až doteď se bere fixed point souřadnice pixelu v textuře, která se zaokrouhlí a vykreslí se barva pixelu na té souřadnici. Při Bilineárním filtrování se tomu dá trochu víc péče : Vezme se fixed point souřadnice texelu a oddělí se jeho celá a desetinná část. Potom se vezme čtveřice texelů, a to ten na souřadnici kterou bychom uvažovali bez filtru a tři na souřadnicích o 1 větších : (větších proto, že desetinná část je vždycky kladná :-) ) Vzorkování při normálním a bilineárně filtrovaném texturování
Mnno.. červeně jsou souřadnice textury ve FP. Fialově je označený pixel, který jsme do teď
brali jako korektní a zeleně jsou pixely, které se použijí pro přefiltrování. Jak se to
dělá ? Pro Filtrování potřebujeme tabulku váhových faktorů, říká se jí "Multab". V ní jsou
uložené váhové faktory všech čtyř texelů pro uřčité odchylky X a Y, pomocí ní se pak určí,
že z prvního texelu se použije 30%, z druhého 45%.. a namíchá se výsledná barva. Jak ale
Multab vygenerovat ? Podle jednoduchých a myslím snadno odvoditelných rovnic se dají váhové
faktory rychle spočítat :
Bylo to zase jako obvykle :-) jednoduché - snad jediný zádrhel je dopočítávání "zbytku", abychom odstranili chyby při zaokrouhlování. Teď se ještě zbývá podívat na texturovací smyčku :
Na začátku se určí index texelu v mapě (tex) a jeho desetinná část (frac), která se skládá z šesti bitů desetinné části v a šesti bitů desetinné části u (0000 VVVV VVUU UUUU). Šest bitů proto, že dva na šestou je 64, jak jsme si předtím určili coby počet odstínů. Pak už se jen odkazuje na multab a texturu. Přibylo ale pár konstant : A_3 je maska textury, to znamená maximální index textury -1 (pro 256×256 je to 0xffff), prostě číslo které zabrání, aby se vlezlo do části paměti kde textura už není, ale zpátky na její začátek. Potom W_0, to je šířka textury a W_1 = W_0 + 1. K čemu to je ? Řekli jsme si že budeme potřebovat čtyři texely, a jejich index je právě [pix] (první), [(pix + 1) & A_3] (ten vpravo od něj) - pozor nemusí být vpravo - pokud se jedná o poslední pixel na řádku, bude to ten vpravo, ale ještě pod ním. [(pix + W_0) & A_3] (ten pod ním) a [(pix + W_0) & A_3] (ten "naproti němu"). Kdybyste opravdu chtěli aby pixely navazovaly správně i na posledním sloupci textury, bude smyčka vypadat takhle :
Význam W_0 zůstane stejný, odpadne W_1 a A_3, ale je to o dost pomalejší. Předchozí verze způsobí jen "skok" v textuře tam kde se opakuje - není to ale moc vidět.. A jak to potom vypadá ? Hezky :-) >> Bilineární filtr Bez filtru
Example umí udělat screenshot (F12), Bilineární filtr se vypíná / zapíná funkcí Switch_Bilerp(int),
kde parametr je "1" nebo "0", podle toho zda chcete filtrování aktivovat / vypnout. Textury
tentokrát mají formát 256color TGA, to je formát který používají američtí hackeři a třeba
Quake ]|[ Arena. Obrázek na TGA můžete převést třeba pomocí ACD-See, ale předtím ho musíte
dát do GIFu, nebo něčeho co musí mít 256 barev, protože TGA může být i truecolor. Stáhnout
si ho (Examplesák + source) můžete tady :
s-buffer engine (šipky značí chybu)
Tož v tomhle díle jste museli stáhnout 350kB obrázků.. To snad není tak hrozný.. No řekněte
sami - už jste viděli někoho zabít člověka kvůli půl mega ? (to budu asi první :-P ...)
No a o čem to bude příště ? Mnno - asi si povíme něco o světle, stínování, stínovacích
modelech a technikách. Tož bye !
-tHE SWINe- |