Ukázka WebGL Grim Reaper

Pár týdnů před Halloweenem 2021 jsem procházel Sketchfab a narazil na skvělý 3D model Grim Reaper od 3DRT. Má rozumné polycount, sadu různých barev a plynulé animace. Padlo tedy rozhodnutí vytvořit živou tapetu s halloweenskou tématikou právě s tímto modelem. Nemohl jsem to však dokončit před Halloweenem, protože jsem postupně přidával nějaké nové efekty a funkce, jejichž implementace a následné vyladění zabralo dost času.

Živé webové demo najdete zde a pro osoby citlivé na blikající světla je zde verze bez blesků. Můžete s ním pracovat kliknutím myši na obrazovku – tím se změní animace. Můžete také vstoupit do režimu volné kamery, který používá navigaci WASD, stisknutím klávesy Enter.

Zdrojový kód je jako obvykle dostupný na Github.

A samozřejmě můžete získat aplikaci živých tapet pro Android.

Kompozice scény

Scéna je docela jednoduchá, takže nevyžaduje žádné třídění objektů – pečlivě zvolené pevně zakódované pořadí vykreslování dosahuje minimálního překreslení:

Nejprve se vykreslí neprůhledné (látka je maskovaná alfa, takže je také neprůhledná) geometrie. Tyto animované objekty používají animaci vrcholů s daty uloženými v texturách FP16, takže pro ukázku je vyžadováno WebGL 2.
Po vykreslení neprůhledných geometrií je zápis do hloubky zakázán pomocí glDepthMask(false) a pak průhledné efekty — kouř, prach a duchové se přes ně prolínají. V této fázi je také nakreslena obloha. Protože jde o nejvzdálenější objekt, nemusí přispívat k hloubce – v zásadě se s ním zachází jako se vzdálenou ořezovou rovinou.

Efekty

To je místo, kde se strávilo většinu času – přemýšlení, vytváření, ladění a odmítání různých efektů pro opravdu jednoduchou scénu s doslova jedinou postavou.

Pokaždé, když jsem měl nápad, jak zlepšit vzhled, přidal jsem ho na nástěnku Trello. Pak jsem měl čas o tom přemýšlet — jak se to bude hodit ke scéně, jak to implementovat atd. Zde je tedy rozpis všech použitých efektů.

Nejprve se do sekačky přidají měkké částice. Polovina z nich stoupá nahoru, polovina klesá dolů zhruba ze středu modelu sekačky, který trochu kolísá v závislosti na animaci. Pro dosažení nejlepšího vizuálního vzhledu jsou samozřejmě použity měkké částice, a proto hloubkový předprůchod. O implementaci měkkých částic si můžete přečíst v jednom z mých předchozích článků.

Poté se vykreslí trochu mihotavého prachu. Můžete si všimnout, že jeho jas je synchronizován s údery blesku – prach obvykle pomalu mizí dovnitř a ven, ale při úderu blesku je viditelnější.

Jako poslední dotek je aplikována poměrně silná viněta. Tento efekt krásně ladí s ponurou atmosférou, pomáhá přitáhnout pozornost ke středu obrazovky a vizuálně skrýt nevýraznou prázdnotu v rozích obrazovky.

Stále existuje několik nápadů na efekty zaznamenané v mé nástěnce Trello, ale myslím si, že jejich přidáním pouze zaplní scénu, aniž by přidával další nápadné pastvy pro oči.

Sky shader

Sky se používá k vyplnění mezery kolem hlavní postavy. Pro přidání dynamiky a pohybu do těchto prázdných částí scény je scéna vykreslena pomocí shaderu, který aplikuje jednoduché zkreslení a blesk na texturu statických mraků.

Pojďme analyzovat kód shaderu. Kombinuje tři jednoduché efekty k vytvoření dynamické oblohy:

  1. Začíná to aplikací barvy na poněkud nevýrazně vypadající texturu základní oblohy ve stupních šedi:

  2. Poté se aplikují vlny z textury malého zkreslení (podobný, ale výraznější efekt lze použít pro vlnění vody). Efekt je jemný, ale znatelně zlepšuje celkový vzhled:

  3. A posledním dotykem je blesk. Aby bylo znovu vytvořeno poněkud realisticky vypadající osvětlení, které nepronikne přes husté mraky, ale prosvítá přes jasné oblasti, jas se zvyšuje exponenciálně – tmavší části získají velmi malý nárůst jasu, zatímco světlé oblasti budou zvýrazněny. Konečný výsledek se všemi efekty vypadá takto:

Časovač pro údery blesku je periodická funkce několika kombinovaných sinusových vln, sepnutých na rozsah [0…2]. K vizualizaci a úpravě koeficientů pro tuto funkci jsem použil opravdu praktickou grafickou kalkulačku Desmos – můžete jasně vidět, že „špičky“ kladných hodnot vytvářejí krátké periodické náhodné shluky:

Obloha se navíc pomalu otáčí, aby bylo pozadí méně statické.

Shader duchů

Strašidelné stezky plující kolem ponurého seka jsou inspirovány tímto návodem k Unreal Engine 4 Niagara — https://www.artstation.com/artwork/ba4mNn.

Prvotní myšlenkou bylo použít geometrii ve tvaru výřezu ze strany válce a otočit ji kolem středu modelu sekačky. Můj bratr však vytvořil shader pro flexibilnější přístup k použití jediné geometrie, kterou lze otáčet v libovolném poloměru a natahovat na libovolnou délku.

Aby toho dosáhl, vertex shader změní geometrii původní sítě. Upravuje souřadnice X a Y vstupního modelu a ohýbá je kolem kružnice daného poloměru. Souřadnice Z nedostává další transformace. Je zodpovědný za vertikální škálování výsledného efektu. (Světový prostor je Z-nahoru). Shader je přizpůsoben pro práci s konkrétním modelem — mozaikovým listem v rovině XZ (všechny souřadnice Y jsou nulové):

Později byla geometrie optimalizována tak, aby těsně zapadla do naší textury sprite, aby se snížilo překreslení:

Na základě matematického výpočtu délky tětivy jsou souřadnice X a Y ohnutého modelu:

x = R * sin(theta);
y = R * cos(theta);

kde theta = rm_Vertex.x / R a R je poloměr ohybu. Théta se však v shaderu vypočítává jinak:

float theta = rm_Vertex.x * lengthToRadius;

lengthToRadius hodnota je jednotná, ale není to jen převrácená hodnota R — můžeme předávat hodnoty větší než 1/R pro úpravu délky efektu (protože se v podstatě jedná o přednásobení rm_Vertex.x ).
Tato drobná změna se provádí za účelem eliminace nadbytečné matematiky pouze pro uniformy v shaderu. Předběžné rozdělení délky podle poloměru se provádí na CPU a tento výsledek je předán do shaderu pomocí lengthToRadius jednotný.
Pokusil jsem se tento efekt vylepšit použitím displacement zkreslení ve fragment shaderu, ale zdá se, že je v pohybu prakticky nepozorovatelné. Ponechali jsme tedy původní jednodušší verzi se statickou texturou, která je také levnější pro GPU.

Filtr s omezeným počtem barev

Není implementováno ve webové verzi, ale v aplikaci pro Android je přítomno snížené následné zpracování barev. Tento drsný efekt dokonale zapadá do celkové atmosféry a dodává scéně tu správnou náladu. Není implementován jako samostatný renderovací průchod po zpracování, ale provádí se ve fragment shaderu, takže vykreslování je stále v podstatě jednoprůchodové.

Je založen na kódu ze hry Q1K3 WebGL https://github.com/phoboslab/q1k3 a vřele doporučuji přečíst si blogový příspěvek o tom, jak udělat zdánlivě nemožné Q1K3 — https://phoboslab.org/log/2021/09 /q1k3-making-of.

Komprese textur

Živá tapeta pro Android se zaměřuje na OpenGL ES 3.0+ a využívá efektivní komprimované textury ETC2 a ASTC. WebGL demo je však optimalizováno pouze pro co nejrychlejší načítání. Opravdu nesnáším, když nějaké jednoduché demo WebGL trvá věčnost, než načte své neopodstatněně obrovské zdroje. Z tohoto důvodu padlo rozhodnutí nepoužívat hardwarově komprimované textury. Místo toho jsou textury komprimovány jako ztrátové WebP. Celková velikost všech aktiv včetně HTML/CSS/JS je pouhých 2,7 MB, takže se načítá docela rychle.
Nedávno bylo také aktualizováno naše demo Hory WebGL s menšími zdroji, ale stále je mnohem větší než Reaper – stahuje 10,8 MB dat při prvním načtení.