Semi-deep dive do virtualizace geometrie v programu na tvorbu videoher UnrealEngine 5.0-5.1
Ve hrách a obecně tvorbě 3D scén byl vždy problém s poměrem množství geometrie a zobrazovaného detailu. I s nejnovějším hardwarem nedokážeme zobrazovat neomezené množství geometrie a tím pádem jí nahrazujeme. Jedná se například o textury jako jsou normal/ bump mapy, které vytváří dojem detailu. Otázkou je, jestli a jak je možné zobrazit tolik detailu a geometrie tak, abychom tyto textury nepotřebovali, jelikož tyto textury zabírají obrovské množství prostoru na disku, následně ve VRam grafické karty. Přestože technologie jako Virtual Texturing pomáhá a byl to obrovský zlom, tak se zvyšujícím se rozlišením obrazovek, zvětšováním světů a množství assetů tohle přestává být udržitelné. A tak se hledají způsoby, jakým je možné toto ovlivnit, změnit – např. používáním scalovaním detailu v zavilosti na blízkosti objektu a množství pixelů, které zabírají na obrazovce. Tento příspěvek trochu rozvíjí systém nanite pro Unreal Engine 5, který tento problém řeší.
(Pokud si chcete ušetřit čtení, podívat se na to do většího detailu, máte 2h volného času a umíte anglicky, tak můžete přejít na https://www.youtube.com/watch?v=TMorJX3Nj6U&ab_channel=UnrealEngine – Inside Unreal od tvůrců UE)
Možnosti scalingu detailu meshů a typu geometrií
1. Voxely
Výhody:
- univerzální
- může držet UV mapy
Nevýhody
- jak je animovat
- co s UV seamy
- co, když je část objektu jen plocha
- čistota při hard-surface modelingu (nutnost držet i původní model pro close-up?
- vysoký datový nárok (až 6x)
- voxel color vs. textura? – nesmysl
- atd.
2.Subdivision/ Multi-ress
Výhody:
- nekonečná jemnost
Nevýhody
- base cage má často mnohem více polygonů než herní assety
- náročné na rendering, switching a drawcally
3. 2D a Vector Dispacement
Výhody:
- někde se dá držet low-poly model opravdu low-poly
Nevýhody
- složitější meshe a chain-linky se nedají vytvořit ani vektorovým displacementem
- super pro organické modely, ale pro hard-surface je extrémně složité, aby mesh displacement fungoval dobře
- texture stretching
4. Point based rendering
Výhody:
- extrémně rychlé
Nevýhody
- obrovský problém s překrýváním = overdraw, pokud nechceme díry modelech – nebo potřebujeme Connecitivity data
- jak můžeme vědět, že malá díra má být nebo ne?
– pokud tam nejsou connectivity data - část řešení by pořád musel být tri-mesh
Trojúhelníky jako základ 3D grafiky (pro dobrý důvod)
Postup a rendering na GPU + nanite
- Podmínky:
- celá scéna je soudržná přes snímky
- Updatuje se jen to, co je nové nebo se mění
- všechny index a vertex data jsou v jednom data poolu
- všechny nanite static meshe jsou v jednom data poolu, takže se data tahají pouze z jednoho místa
- rasterizují se trojúhelníky
- z GPU se vyřazují instance – načítá se pouze 1 (využití opakování assetů)
- hloubka celé scény se tedy načítá pouze jednou – DrawIndirect
Klasické LOD
Ve hrách je používají systémy LOD (level of detail), které se přepínají v závislosti na blízkosti ke kameře v několika úrovních. Obrovskou nevýhodou LOD-based assetů je nutnost vytvořit několik těchto úrovní detailu pro každý asset, k tomu odpovídající UV apod. Je to obrovský konzument času a herní studia stojí vysoké množství financí – platy 3D grafiků.
LOD 1, 2 a 3
LOD trojúhelníkové klasty nanite meshe
Pro (zatím) statické meshe nanite vytvoří při importování assetu systém několika LOD klastů – spojení trojúhelníků ve funkční celky – které se zobrazují v závislosti na tom, kolik místa zabírají na obrazovce aka kolik pixelů zobrazuje klastr. První taková myšlenka vznikla už před rokem 2000, ale nikdy se neimplementovala.
Ořezávání neviditelných klastrů
Pokud zaměříme naši optimalizaci množství geometrie a detailu na reálné množství pixelů, které zabírají na obrazovce, poté je jasné, že nepotřebujeme kreslit trojúhelníky, které nevidíme.
K tomu se využívá frustrum (boundary) culling a dvojí occlusion culling
Na prvním snímku kreslím všechnu geometrii co vidím. Pokud se pohnu do snímku 2 o pár cm/ metrů, využiji Hierarchy Z-Buffer (HZB) z minulého snímku, vyvolám nový HZB a porovnám, co je nové a co už je nakreslené. To mi dovolí kreslit jen ty klastry, co jsou nové a ušetřit tak výkon.
Visibility buffer
Když máme HZB, tak se můžeme posunout dále. Potřebujeme rasterizovat geometrii a textury/ materiály (do texturování a materiálů zde nebudeme zabíhat, to je téma samo pro sebe).
Máme hloubku a teď zapíšeme InstanceID a TriangleID
- Načteme Visibility Buffer (v podstatě co vidíme) a načtou se trojúhelníková data
- Trojúhelník se skládá ze 3 vertexů a ty se zapíšou na obrazovku do pixelu – s tím i vertex attributy (může to být i shader)
- Všechny materiály mohu vyvolat v jednom DrawCallu
Klastrová hierarchie
Ve chvíli kdy máme HZB, máme materiály a máme geodata, tak přichází na řadu LOD. A Jak už bylo řečeno, není potřeba kreslit “neomezený počet trojúhleníků”, pokud máme omezený počet pixelů (při 4K zobrazení to je 8 294 400 pixelů, což v dnešní době není pro grafické karty problém vykreslit).
Důležité tedy je, že tento přístup volá po konzistentním množství trojúhelníků, posléze klastrů. Z toho nám vyplývá, že LOD by mělo být závislé na pohledu a ne vzdálenosti od kamery, jako tomu je u klasických herních assetů.
Trhliny mezi klastry
Pokud máme nezávislé LOD-klastry, tak se při prohazování LOD mohou vytvořit trhliny (jeden klastr se změní a druhý ne a kvůli redukci trojúhelníků vznikne díra – Co s tím?
- uzamknout hranice klastrů? – NE!
- To by pak nebylo možné snižovat na minimum množství klastrů v meshi
- Spojování LOD-klastrů do skupin – ANO!
- Poté můžu odemknout hranice, protože dělají stejná rozhodnutí, pokud hranice (edge) projde kolapsem pro zjednodušení meshe (siplification)
- V tu chvíli již nejsou nezávislé – téměř se vracíme ke klasickým LOD systémům – trhliny se dají detekovat při vytváření nanite-meshe a uzamknutí je pak variabilní a používá se jen na místech, kde klastry mohou vytvořit trhlinu a ne pro celý nanite mesh
Spojování klastrů
Jak se tedy rozhodnu, jaké klastry spojit?
Možností je více. Ideální je spojit ty, které mají nejvíce společných hranic (edges), abychom omezili možnost vzniku trhlin. Existují pro to také METIS libraries – knihovny používané k optimalizaci těchto úkonů – je zde spousta složitých výpočtů a problémů, které je si potřeba hlídat – uzamčené hrany atd. (ostré překryvy (např. převisy střech) – spousta způsobů, jak optimalizovat – do toho zabíhat nebudeme.
Ideální situace ale vzniká tehdy, kdy je minimální počet povinných hranic mezi klastry, počet trojúhelníků v klastru je roven nebo co nejblíže číslu 128, ale nikdy více, aby se geometrie mohla rasterizovat. Je také dáno určité množství vertexů, přes které se klastr nesmí dostat kvůli material shaderům a v neposlední řadě je také dobré minimalizovat počet hranic (edgů) mezi klastry, aby bylo snazší je spojovat a systém běžel rychleji.
Systém spojování klastrů lze ilustrovat na obrázku nahoře. máme 4 sousedící klastry a ty spojíme do jednoho, vydělíme počet trojúhelníků na polovinu a rozdělíme tento klastry na 2. Hranice mezi těmito 2 klastry bude co nejdelší a bude tam co nejméně edgů.
Jaký LOD klastr použít?
Využití klastrů a jejich detailu závisí na více faktorech
- jak víme, je kompletně lokální – nezávisí na celé scéně
- závisí na chybě v zobrazování
- jde o odhad chyby vykreslování při zobrazení a viditelnosti této chyby pro uživatele
- pokud uskupení klastrů vykazuje v nějakém místě moc velkou chybu a uskupení je na sobě závislé, poté se vykreslí společně požadovaný detail s nejmenší chybou vykreslení
- atd.
Jak dosáhnou “nepostřehnutelnosti” přechodu LOD
Jak je možné, že si lokální změny geometrie nevšimnu?
Trojúhelníky/ klastry, které kreslíme (DrawCall) jsou velké maximálně jako 1 pixel – přechod tedy zajištuje jednoduše Temporal anti-aliasing (TAA)
- kombinace předchozího snímku a vyhlazení přechodu/ změny – v dnešní době standard pro video
- pokud je změna na úrovni pixelu, nemůžu si změny prakticky všimnout!
TAA může vykazovat rovněž nějaké artefakty a chyby, jako třeba ghosting, ale systémy se zlepšují, takže TAA standard není
- MSAA (Multi-Sample AA)
- SSAA (Super Sampling AA – renderuje obrázek ve větším rozlišení a pak ho downsampluje – náročné na GPU)
- FXAA (Fast Approximate AA) – rychlé, ale hodně rozmazané, pro LowEnd PC
- MLAA (Morphological AA) – náročné na HW, rozpíjí hranice
- SMAA (Subpixel Methodological AA) – podobné FXAA a MLAA, ale redukuje rozpíjení na minimum
- TXAA/ TAA (Temporal AA) – náročné na HW, ale velmi efektivní s dobrými výsledky
- DLSS (Deep Learning Super-Sampling)
Rasterizace
Systém převedení vektorového prostředí do 2D obrazu HW vs. SW rasterizace.
Zahrnuto do Drawcallu pokud
- trojúhleník je vykreslen, pokud střed pixelu leží v jeho obsahu nebo na jeho hraně (edge)
- tímto se vyvarujeme overdrawingu
SW rasterizace pro malé trojúhelníky/ klastry. Je efektivnější pro drobné trojúhelníky velikosti pixelů apod. Dříve se myslelo, že to je pomalejší než HW rasterizace, ale posun v technologiích to již překonal a pro menší trojúhelníky je dnes 3x rychlejší než HW rasterizace.
HW rasterizace pro velké trojúhelníky/ klasty – zjednodušeně – prování grafická karta.
Overdraw
Pokud používáme HZB z minulého snímku a vyvoláváme druhý na vykreslení toho, co je nové – GBuffer call, tak zákonitě vznikne overdraw v místech, kde je vysoké množství překrývajících se assetů/ klastrů. Další problém je, že pokud se facy meshů překrývají o méně než velikost klastrů, tak se vykreslují oba v GBufferu.
Ideální je se překrývání vyhnout co nejvíce – určitě to nejde úplně, ale hodně overdraws může způsobit snížení frameratu.
Vykresluji červený klastr, žlutý i modrý, i když z červeného vidím jen polovinu a část žlutého taky není vidět. Čím jsou větší trojúhelníky, tím budou větší klastry – lower-poly meshe nakonec mohou být náročnější pro nanite!
Malé instance
Na jeden klastr připadá cca 128 trojúhelníků a jedná se nejmenší jednotku. Co tedy dělat, když se mesh zmenší na 1 klastr a nemůže jít na menší množství?
To zatím nemá řešení, ale nabízí se možnosti spojování (merging), hierarchální instancování (instance instancí) apod.
Materiály, UV, IDs etc.
Materiály a shadow mapy jsou další částí nanite systému, který by si vyžádal nejen další příspěvek, ale samostatný vysvětlování s hlubším porozuměním systému nanite a streamingu dat. Pokud je to pro vás zajímavé, doporučuji:
Doporučuji: https://www.youtube.com/watch?v=TMorJX3Nj6U&ab_channel=UnrealEngine – Inside Unreal od tvůrců UE
A k čemu vám to je?
- Složité geometrie při práci
- usnadnění vizualizací
- možnost pracovat s neomezeným detailem
- ušetření místa – nepotřebuji normal mapy
- vyhlídka na mnohem detailnější hry
- ušetření práce s optimalizací pro ty, kteří budou dělat s VR nebo budou pracovat třeba jako herní designéři apod.
- filmová studia a nezávislí tvůrci mohou strávit více času na tvorbě obsahu a neztrácet čas na úpravách meshů a LODy
- online rendering za minimální cenu
- možnost tvořit videa pro klienty
- způsob, jak si uvařit závity v mozku, když se to vše snažíte pochopit
atd. atd. atd.
Zdroje a inspirace pro další bádání
https://www.nvidia.com/content/dam/en-zz/Solutions/events/siggraph2018/pdf/tuesday/sig1824-adam-marrs-rahul-sathe-adaptive-temporal-antialiasing.pdf
https://www.youtube.com/watch?v=3J3gq9BJbKE&ab_channel=GreenleafVision
https://advances.realtimerendering.com/s2021/Karis_Nanite_SIGGRAPH_Advances_2021_final.pdf
https://news.itmo.ru/en/startups_and_business/innovations/news/9473/
https://www.unrealengine.com/en-US/blog/understanding-nanite—unreal-engine-5-s-new-virtualized-geometry-system
https://www.youtube.com/watch?v=P65cADzsP8Q&ab_channel=WilliamFaucher
https://docs.unrealengine.com/5.0/en-US/nanite-virtualized-geometry-in-unreal-engine/
https://www.elopezr.com/a-macro-view-of-nanite/
https://docs.unrealengine.com/5.0/en-US/virtual-shadow-maps-in-unreal-engine/
https://cdn2.unrealengine.com/nanite-for-educators-and-students-2-b01ced77f058.pdf
https://www.youtube.com/watch?v=NRnj_lnpORU&t=5489s&ab_channel=High-PerformanceGraphics
https://www.youtube.com/watch?v=eviSykqSUUw&t=1523s&ab_channel=SIGGRAPHAdvancesinReal-TimeRendering
https://www.youtube.com/watch?v=TMorJX3Nj6U&ab_channel=UnrealEngine
https://www.youtube.com/watch?v=Dc1PPYl2uxA&ab_channel=UnrealEngine
https://www.youtube.com/watch?v=xUUSsXswyZM&ab_channel=UnrealEngine
Blender
Unreal Engine 5.0
Unreal Engine 5.1