mipmapa (z lat. multum in parvo)TakéMip mapa, MIP mapping, Mipmapping, Mip levelPokročilý
Definice
Mipmapa je předpočítaná řada postupně zmenšovaných kopií jedné textury, ve které má každá úroveň poloviční rozměry oproti předchozí. Grafická karta při vykreslování vybírá úroveň podle toho, jak hustě se texel promítá na pixel, čímž potlačuje aliasing na vzdálených plochách a zároveň zlepšuje využití texturové cache.
Než se na to spolehnete: Wikidata ID pro Mipmap jsem uvedl podle paměti, prosím ověřit; pokud neodpovídá, stačí položku ze sameAs odstranit.
Proč vznikly zmenšené kopie textury
Mipmapa řeší situaci, kdy se na jeden pixel obrazovky promítne mnoho texelů textury. Bez ní vzorkovač odebere jednu jedinou hodnotu z plné rozlišení textury a zbytek prostě zahodí. Výsledkem je aliasing: šachovnicová podlaha v dálce se rozpadne na blikající moaré, které se s každým pohybem kamery mění. Mipmapa nabídne místo toho úroveň, jejíž texely už předem obsahují průměr původní oblasti, takže vzorek reprezentuje celou plochu pixelu.
Jak se řada úrovní staví
Úroveň 0 je originál, například 1024 × 1024. Každá další úroveň má poloviční šířku i výšku (512, 256, 128 …) až po úroveň 1 × 1. Filtrace při zmenšování je typicky box filter nebo kvalitnější Kaiser či Lanczos. Celá pyramida stojí o zhruba třetinu paměti navíc oproti samotné základní úrovni, protože součet ploch 1/4 + 1/16 + … konverguje k 1/3.
Mipmapy lze vygenerovat za běhu (v OpenGL glGenerateMipmap, ve Vulkanu ručně řetězcem blitů), ale u produkčních assetů se běžně předpočítávají offline do komprimovaných formátů jako BC7 nebo ASTC, protože kompresi je lepší dělat pro každou úroveň zvlášť než komprimovat a pak zmenšovat.
Podle čeho GPU vybírá úroveň
Rasterizér počítá derivace texturových souřadnic mezi sousedními pixely v kvádru 2 × 2. Z nich vyjde měřítko, jak rychle se souřadnice mění, a jeho logaritmus o základu 2 dává spojitou hodnotu zvanou level of detail. Trilineární filtrace pak vzorkuje dvě sousední celočíselné úrovně bilineárně a mezi nimi interpoluje, aby na obrazovce nebyl vidět ostrý přechod. Vývojář může výběr posunout parametrem LOD bias nebo úroveň vynutit explicitně, což se hodí například u ručně řízeného rozostření.
Kde mipmapa přestává stačit
Standardní mipmapa je izotropní: předpokládá, že se texel zmenšuje ve všech směrech stejně. U ploch vidíných pod ostrým úhlem, typicky silnice mizící k horizontu, je zmenšení podél jedné osy mnohem větší než podél druhé. GPU musí zvolit hrubší úroveň podle většího ze zmenšení, a povrch se jeví rozmazaný. Právě proto existuje anizotropní filtrace, která odebere víc vzorků podél protáhlé osy z jemnější úrovně.
Druhým typickým problémem je prosakování barev mezi dlaždicemi v atlasu. Při zmenšování se sousední, nesouvisející oblasti smíchají a v dálce se objeví cizí barva na okraji sprite. Řešením je dostatečný padding kolem každé dlaždice, samostatné textury nebo texturové pole.
Vliv na výkon a paměť
Mipmapa není jen kvalitativní vylepšení. Vzdálený objekt čte z malé úrovně, která se celá vejde do texturové cache, takže dramaticky klesá počet výpadků cache a přenos z paměti. Streamovací systémy toho využívají: do VRAM se nahrají jen nejhrubší úrovně a jemné se dotahují, až když se kamera přiblíží. Vypnutí mipmap proto zpravidla zhorší jak obraz, tak snímkovou frekvenci.
Příklady z praxe
Blikající šachovnicová podlaha ve hře
Level designér nastaví podlaze texturu s jemným vzorem dlaždic a vypne mipmapy, aby byla v detailu ostřejší. Při chůzi hráče se vzdálená část podlahy začne rozpadat na pohyblivé moaré. Zapnutí mipmap s trilineární filtrací blikání odstraní, ale podlaha pod ostrým úhlem zůstane rozmazaná, dokud se nepřidá anizotropní filtrace 8x nebo 16x.
Generování mipmap při nahrání textury v OpenGL
Po nahrání základní úrovně stačí vyžádat automatické dopočítání pyramidy a nastavit minifikační filtr na trilineární režim. Bez GL_LINEAR_MIPMAP_LINEAR by ovladač zůstal u úrovně 0 a vygenerované úrovně by se nikdy nepoužily, což je jedna z nejčastějších chyb u vlastního rendereru.
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 1024, 1024, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); glGenerateMipmap(GL_TEXTURE_2D); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); // trilineární glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
Časté omyly
- MýtusMipmapy zabírají dvakrát tolik paměti, radši je vypnu.
- Ve skutečnostiCelá pyramida přidá k základní úrovni jen zhruba 33 % dat, protože plochy dalších úrovní tvoří geometrickou řadu s podílem 1/4. Navíc díky lepšímu využití cache bývá výsledek rychlejší než textura bez mipmap.
- MýtusMipmapa zbytečně rozmazává obraz, ostřejší je vzorkovat vždy z originálu.
- Ve skutečnostiVzorkování z originálu při silném zmenšení není ostřejší, ale nesprávné: podvzorkovaný signál vytváří aliasing a blikání v pohybu. Rozmazání pod ostrým úhlem není chybou mipmapy jako takové, řeší ho anizotropní filtrace.
- MýtusMipmapy má smysl dělat jen pro 3D scény.
- Ve skutečnostiMipmapa pomáhá kdekoli, kde se textura zobrazuje menší než v nativním rozlišení, tedy i u 2D sprite scaling, mapových dlaždic nebo náhledů v UI. Rozdílem je jen to, že u pixel-artu se často volí nearest filtr záměrně.
Časté dotazy
- Kolik paměti mipmapa navíc zabere?
- Mipmapa zvětší paměťovou stopu textury přibližně o 33 procent. Každá úroveň má poloviční rozměry, tedy čtvrtinovou plochu, a součet řady 1/4 + 1/16 + 1/64 a dále se blíží jedné třetině základní úrovně. U textury 1024 × 1024 v RGBA8, která zabírá 4 MB, tedy celá pyramida přidá zhruba 1,3 MB. V praxi je to výhodná investice, protože vzdálené objekty čtou z malých úrovní, které se vejdou do texturové cache, takže klesá objem přenosů z videopaměti.
- Musí mít textura pro mipmapy rozměr mocniny dvou?
- Mipmapa dnes nevyžaduje rozměry v mocninách dvou. Moderní grafická API i mobilní GPU zvládají non-power-of-two textury a řetězec úrovní se u nich tvoří dělením dvěma se zaokrouhlením dolů, dokud se nedojde k 1 × 1. U starších rozhraní a některých kompresních formátů ale nemocninné rozměry vedou k nepříjemným zaokrouhlovacím posunům mezi úrovněmi nebo k nutnosti doplnit okraje. Právě proto se v produkci stále běžně drží rozměry jako 512, 1024 nebo 2048.
- Kdy trilineární filtrace nestačí a je potřeba anizotropní?
- Trilineární filtrace nestačí ve chvíli, kdy se plocha promítá pod ostrým úhlem k pohledu, typicky u podlah, silnic nebo terénu mizejícího k horizontu. Výběr úrovně vychází z většího ze zmenšení podél os, takže povrch je podél druhé osy zbytečně hrubě filtrovaný a působí rozmazaně. Anizotropní filtrace tento případ řeší tím, že odebere více vzorků podél protáhlého směru z jemnější úrovně. Její cena je poměrně nízká, proto se běžně nabízí jako uživatelské nastavení 2x až 16x.
- Proč se v atlasu objevují cizí barvy na okrajích sprite?
- Mipmapa při zmenšování průměruje sousední texely bez ohledu na to, že v atlasu spolu logicky nesouvisejí. Na vyšších úrovních se tak barva jedné dlaždice promíchá se sousední a na vzdáleném objektu se objeví barevný lem. Řešením je nechat kolem každé dlaždice dostatečný padding, ideálně tak široký, aby přežil i nejhrubší používanou úroveň, nebo nahradit atlas texturovým polem, kde se jednotlivé vrstvy při filtraci nemíchají. Poslední možností je omezit počet generovaných úrovní.
- Kdy je lepší mipmapy negenerovat?
- Mipmapy nedávají smysl u textur, které se vždy zobrazují v poměru jedna ku jedné nebo zvětšené: fullscreen render targety, G-buffery, lookup tabulky, texty vykreslované v nativním rozlišení nebo pixel-art, u kterého je zmenšení stejně nežádoucí. U render targetů navíc generování pyramidy po každém snímku stojí čas GPU. Výjimkou jsou případy, kdy se z mipmap dělá efekt záměrně, například bloom nebo přibližná odrazová mapa, kde hrubší úrovně slouží jako levné rozostření.
Zdroje
- OpenGL 4.6 Core Profile Specification(otevře se v novém okně)
- Mipmap(otevře se v novém okně)
- Direct3D 11 Textures(otevře se v novém okně)