Poglavlje 47: Shader Complexity Optimizacija
"Najskuplji shader je onaj za koji niste ni znali da je skup."
Uvod
Stigli smo do jednog od najvaznijih poglavlja u celoj knjizi. Mozete imati savrseno optimizovanu geometriju, vrhunski LOD sistem, i besprekoran Nanite pipeline — ali ako vasi materijali gutaju GPU cikluse kao da sutra ne postoji, sve to pada u vodu. Shader complexity optimizacija je vestina koja razdvaja pocetnike od profesionalaca u Unreal Engine 5 razvoju.
U prethodnim poglavljima smo postavili temelje: u Poglavlju 17 smo naucili kako shaderi funkcionisu na fundamentalnom nivou, u Poglavlju 21 smo prosli kroz teoriju shader optimizacije, a u Poglavlju 22 smo detaljno upoznali UE5 material system. Sada je vreme da sve to spojimo u prakticnu, merljivu optimizaciju.
Ovo poglavlje ce vas provesti kroz kompletan workflow: od identifikovanja problematicnih materijala, preko sistematskog smanjivanja njihove cene, do postavljanja kvalitetnih per-platform budzeta. Na kraju poglavlja, uzecemo jedan "tezak" materijal i korak po korak ga optimizovati — bez zrtvovanja vizuelnog kvaliteta.
Hajde da krenemo.
47.1 Zasto je Shader Complexity Vazan
Pre nego sto uronimo u alate i tehnike, hajde da razumemo zasto je ovo toliko bitno.
47.1.1 GPU Pipeline i Pixel Shaderi
Kada GPU renderuje scenu, svaki piksel na ekranu mora proci kroz pixel shader (fragment shader). Taj shader izracunava konacnu boju piksela na osnovu materijala koji je dodeljen objektu. Sto je materijal kompleksniji — vise teksturnih sample-ova, slozenija matematika, vise feature-ova — to GPU trosi vise vremena po pikselu.
Hajde da stavimo to u perspektivu:
- Na 1920x1080 rezoluciji imate 2.073.600 piksela
- Na 2560x1440 imate 3.686.400 piksela
- Na 3840x2160 (4K) imate 8.294.400 piksela
Svaki od tih piksela mora proci kroz shader. Ako vas shader trosi 50 instrukcija umesto 20, to je 2.5x vise posla — pomnozeno sa milionima piksela. A to je pre nego sto uracunamo overdraw, gde se isti piksel renderuje vise puta zbog preklapajuce geometrije.
47.1.2 Prakticni Uticaj na Performanse
U praksi, shader complexity direktno utice na:
- Frame time — koliko dugo traje renderovanje jednog frejma
- GPU utilization — koliki procenat GPU-a je zauzet shader execution-om
- Thermal throttling — posebno na mobilnim platformama, kompleksni shaderi generisu vise toplote
- Battery life — na mobilnim uredjajima, svaka nepotrebna instrukcija trosi bateriju
Tipican "budget" za pixel shader u AAA igri na konzolama je oko 100-150 instrukcija za vecinu materijala, sa retkim izuzecima do 200-250 za hero materijale (likovi, kljucni objekti). Na mobilnim platformama, taj budzet pada na 30-60 instrukcija.
47.1.3 Ljudska Percepcija vs. GPU Trosak
Evo kljucne istine koju mnogi developeri zaboravljaju: igrac ne vidi instrukcije, vidi piksele. Materijal koji koristi 200 instrukcija i materijal koji koristi 80 instrukcija mogu izgledati identicno igracu — ali razlika u performansama je ogromna.
Nas posao kao tehnickih umetnika i programera je da nadjemo tacku gde smanjivanje kompleksnosti ne utice na percepciju igraca. To je sustina ovog poglavlja.
47.2 Shader Complexity View Mode
Unreal Engine 5 dolazi sa ugradenim alatom koji nam omogucava da vizuelno vidimo koliko je svaki materijal "skup". To je Shader Complexity view mode.
47.2.1 Kako Pristupiti Shader Complexity Prikazu
U UE5 editoru, shader complexity view mode se aktivira na sledeci nacin:
- U viewport-u, kliknite na padajuci meni za view mode (podrazumevano pise "Lit")
- Idite na Optimization Viewmodes
- Izaberite Shader Complexity
Alternativno, mozete koristiti precicu: pritisnite Alt + 8 u viewport-u.
Kada aktivirate ovaj mode, cela scena ce se prikazati u boji koja odrazava relativnu cenu svakog piksela.
47.2.2 Citanje Heat Map-e — Sta Boje Znace
Shader complexity heat map koristi spektar boja od zelene do bele, gde svaka boja oznacava odredjen nivo kompleksnosti:
Zelena (Green) — Jeftino
Zelena boja oznacava materijale niske kompleksnosti. To su vasi "radni konji" — materijali koji pokrivaju veliku vecinu scene i ne stvaraju probleme. Tipicno:
- Manji od 50 instrukcija
- Mali broj texture sample-ova (1-3)
- Jednostavna matematika
- Nema specijalnih feature-ova
Primer: Jednostavan opaque materijal sa Base Color teksturom, Roughness/Metallic packed teksturom, i Normal Map-om. Tri texture sample-a, skoro nikakva dodatna matematika.
Zeleni materijali su idealni za pozadinsku geometriju, podove, zidove, teren na daljini — sve sto pokriva veliku povrsinu ekrana.
Zuta (Yellow) — Umeren Trosak
Zuta boja oznacava materijale srednje kompleksnosti. Ovi materijali su prihvatljivi za objekte srednje vaznosti, ali treba paziti da ne pokrivaju preveliku povrsinu ekrana:
- 50-100 instrukcija
- Umeren broj texture sample-ova (4-8)
- Neka dodatna matematika (blending, detaljne normale, jednostavan parallax)
- Mozda jedan ili dva dodatna feature-a
Primer: Materijal za zid sa Base Color, Normal Map, Roughness/Metallic, plus detail normal tiling i blend izmedju dve teksture na osnovu vertex color-a.
Zuti materijali su OK za srednji plan scene — objekte koji su dovoljno blizu da se vide detalji, ali nisu primarni fokus kamere.
Crvena (Red) — Skupo
Crvena boja je upozorenje. Ovi materijali su skupi i treba da budu ograniceni na male povrsine ekrana ili na objekte od posebnog znacaja:
- 100-200 instrukcija
- Veliki broj texture sample-ova (8-16)
- Kompleksna matematika (parallax occlusion mapping, subsurface scattering, kompleksan blending)
- Vise aktivnih feature-ova
Primer: Character skin materijal sa subsurface scattering-om, detail normal-ama, roughness varijacijama, i custom lighting model-om.
Crveni materijali su prihvatljivi za hero objekte — glavni likovi, kljucni predmeti u sceni, elementi kojima igrac posvecuje paznju. Ali ako vidite crveno na zidovima ili podu, imate problem.
Bela (White) — Kriticno Skupo
Bela boja je alarm. Ovo znaci da materijal koristi ekstremno veliki broj instrukcija i skoro sigurno predstavlja performance bottleneck:
- Preko 200 instrukcija
- Ogroman broj texture sample-ova (16+)
- Veoma kompleksna matematika
- Mnogo aktivnih feature-ova, mozda ukljucujuci i raymarching ili proceduralne efekte
Primer: Materijal sa punim parallax occlusion mapping-om, tri sloja blendovanih tekstura sa po 4 sample-a svaki, plus proceduralno generisani patern i custom lighting.
Beli materijali su gotovo uvek kandidati za optimizaciju. Jedini izuzetak mogu biti specijalni efekti koji pokrivaju mali deo ekrana i pojavljuju se kratkotrajno (eksplozije, magic spells).
Ruzicasta/Pink (Magenta) — Overdraw Indikator
U nekim konfiguracijama, ruzicasta/magenta boja se pojavljuje kada kombinacija shader complexity-ja i overdraw-a dosegne kriticne nivoe. Ovo je najopasnija situacija jer oznacava da ne samo da je shader skup, vec se isti piksel renderuje vise puta sa tim skupim shader-om.
47.2.3 Prakticno Tumacenje Heat Map-e
Evo kako treba interpretirati ono sto vidite:
Zdrava scena izgleda ovako:
- ~70-80% scene je zeleno
- ~15-20% je zuto
- ~5-10% je crveno
- Belo se pojavljuje retko ili nikako
Problematicna scena izgleda ovako:
- Manje od 50% je zeleno
- Veliki delovi su zuti ili crveni
- Belo se pojavljuje na vecim povrsinima
- Posebno problematicno: crveno/belo na pozadinskim objektima koji pokrivaju veliki deo ekrana
47.2.4 Ogranicenja Shader Complexity View-a
Vazno je razumeti sta ovaj prikaz ne pokazuje:
- Ne pokazuje apsolutne brojeve — boje su relativne i zavise od konfiguracije
- Ne uzima u obzir screen coverage — materijal moze biti crven ali pokrivati samo 10 piksela, sto je zanemarljivo
- Ne pokazuje sve GPU troskove — vertex shader cost, tessellation, compute shaderi nisu prikazani
- Moze varirati izmedju platformi — isti materijal moze imati razlicit instruction count na razlicitim GPU-ovima
Zato koristimo shader complexity view kao pocetnu tacku, a ne kao jedini dijagnosticki alat.
47.3 Quad Overdraw View
Dok nam Shader Complexity View pokazuje koliko je svaki materijal skup sam po sebi, Quad Overdraw View nam otkriva koliko puta se isti piksel renderuje — sto je jednako vazan (a cesto i vazniji) faktor u ukupnim performansama.
47.3.1 Sta je Quad Overdraw
Moderni GPU-ovi ne procesiraju piksele pojedinacno — oni ih procesiraju u grupama od 2x2 piksela, koje se zovu quad-ovi. Kada se dva objekta preklapaju na ekranu, piksel koji je "ispod" se renderuje uzalud jer ce biti prebrisan pikselom objekta koji je "iznad".
Overdraw se desava u vise situacija:
- Preklapajuca geometrija — zidovi iza zidova, objekti iza drugih objekata
- Transparentni materijali — svaki sloj transparencije zahteva zasebno renderovanje
- Masked materijali — pikseli koji se discard-uju i dalje zauzimaju quad-ove
- Particle sistemi — posebno problematican izvor overdraw-a
47.3.2 Kako Aktivirati Quad Overdraw View
U UE5 editoru:
- Kliknite na view mode padajuci meni
- Idite na Optimization Viewmodes
- Izaberite Quad Overdraw
47.3.3 Citanje Quad Overdraw Prikaza
Quad Overdraw view koristi slican color-coding kao Shader Complexity:
- Tamno zelena — Overdraw 1x (idealno — svaki piksel se renderuje tacno jednom)
- Svetlo zelena — Overdraw ~2x (prihvatljivo za vecinu scena)
- Zuta — Overdraw ~3-4x (treba obratiti paznju)
- Crvena — Overdraw 5x+ (problematicno)
- Bela — Overdraw 8x+ (kriticno — ozbiljan performance problem)
47.3.4 Kombinovanje Sa Shader Complexity
Prava moc dolazi kada kombinujete informacije iz oba view mode-a. Zamislite sledeci scenario:
Scenario A: Materijal koji koristi 30 instrukcija (zelen u Shader Complexity), ali se pojavljuje sa overdraw-om 6x (crven u Quad Overdraw). Efektivni trosak: 30 * 6 = 180 instrukcija po pikselu.
Scenario B: Materijal koji koristi 150 instrukcija (crven u Shader Complexity), ali nema overdraw (zelen u Quad Overdraw). Efektivni trosak: 150 * 1 = 150 instrukcija po pikselu.
Scenario A je zapravo skuplji od Scenaria B! Zato je vazno gledati oba aspekta.
47.3.5 Shader Complexity With Quad Overdraw
UE5 takodje nudi kombinovani view mode koji se zove Shader Complexity with Quad Overdraw (nekada se naziva i "Shader Complexity & Quads"). Ovaj mode mnozi shader complexity sa overdraw-om za svaki piksel, dajuci vam najtacniju sliku ukupnog piksek shader troska.
Aktivira se na isti nacin — kroz Optimization Viewmodes meni u viewport-u.
Ovo je najkorisniji od tri view mode-a za generalno profilisanje jer vam daje celokupnu sliku. Koristite ga kao prvi korak, a zatim se prebacite na individualne view mode-ove za detaljniju dijagnostiku.
47.4 Shader Statistics i Instruction Count
Pored vizuelnih view mode-ova, UE5 Material Editor vam daje precizne numericke podatke o svakom materijalu.
47.4.1 Material Stats Panel
Kada otvorite materijal u Material Editor-u, u donjem delu prozora postoji Stats panel. Ovaj panel prikazuje:
- Base pass instruction count — broj instrukcija za glavni rendering pass
- Vertex shader instruction count — broj instrukcija za vertex shader
- Texture sample count — ukupan broj teksturnih sample-ova
- Shader compilation status — za koje platforme je shader kompajliran
- Interpolator count — koliko podataka se prenosi iz vertex u pixel shader
Ove informacije su dostupne za svaku platformu i quality level posebno (kliknite na odgovarajuci tab u Stats panelu).
47.4.2 Instruction Count Po Platformi
Isti materijal moze imati razlicit instruction count na razlicitim platformama:
| Platforma | Tipican Instruction Count za isti materijal |
|---|---|
| Windows (SM5) | 85 instrukcija |
| Windows (SM6) | 78 instrukcija |
| PlayStation 5 | 82 instrukcije |
| Xbox Series X | 80 instrukcija |
| Nintendo Switch | 92 instrukcije |
| Android (Vulkan) | 105 instrukcija |
| iOS (Metal) | 88 instrukcija |
Razlike nastaju zbog razlicitih shader kompajlera, GPU arhitektura, i podrzanih feature-ova. Zato je bitno proveravati instruction count za svaku ciljnu platformu.
47.4.3 Sta Instruction Count Zapravo Znaci
Instruction count je aproksimacija, ne apsolutna mera performansi. Evo zasto:
- Razlicite instrukcije imaju razlicite troskove — texture sample je obicno mnogo skuplji od aritmeticke operacije
- GPU paralelizam — moderni GPU-ovi mogu izvrsavati neke instrukcije paralelno
- Cache behavior — texture sample moze biti brz (cache hit) ili spor (cache miss)
- Latency hiding — GPU moze sakriti latenciju jedne operacije izvrsavajuci drugu
Ipak, instruction count je korisna metrika za poredjenje. Ako materijal A ima 80 instrukcija a materijal B ima 160, materijal B je skoro sigurno skuplji — mozda ne tacno 2x, ali definitivno skuplji.
47.4.4 Platform Shader Info Prozor
Za jos detaljnije informacije o kompajliranom shaderu, mozete koristiti Platform Shader Info prozor. Do njega dolazite iz Material Editor-a klikom na Window > Platform Stats ili desnim klikom na output node i odabirom Preview Platform Stats.
Ovaj prozor pokazuje:
- Detaljan assembly za svaku platformu
- Registar koristenje (register usage)
- Stvarni GPU instruction count (ne estimirani)
- Texturne operacije sa detaljima o filtriranju
Ovo je napredniji alat koji je koristan kada treba da razumete zasto je shader skup, ne samo koliko je skup.
47.5 Strategije za Smanjivanje Shader Complexity-ja
Sada dolazimo do srzi poglavlja — konkretne tehnike za smanjivanje cene materijala. Ove tehnike su poredane od najjednostavnijih (i najuticajnijih) do najslozenijih.
47.5.1 Uklanjanje Nepotrebnih Feature-ova
Ovo je najjednostavnija i cesto najefektivnija optimizacija. Princip je prost: ako igrac ne moze da vidi razliku, iskljucite feature.
Parallax Occlusion Mapping na Udaljenim Objektima
Parallax Occlusion Mapping (POM) je jedan od najskupljih feature-ova u materijalima. Koristi raymarching u texture space-u za simuliranje dubine, sto zahteva veliki broj texture sample-ova (obicno 8-32 koraka, svaki sa sopstvenim sample-om).
Problem: POM na zidu koji je 20 metara od kamere trosi isto GPU ciklusa koliko POM na zidu koji je 1 metar od kamere — ali igrac ne vidi razliku.
Resenje:
// U material graph-u:
// Koristite Distance-based switch
float CameraDistance = length(CameraPosition - WorldPosition);
float POMFade = saturate((CameraDistance - POM_FadeStart) / (POM_FadeEnd - POM_FadeStart));
// Kada je POMFade = 0, koristimo pun POM
// Kada je POMFade = 1, koristimo obicnu Normal Map-u
// Izmedju — blendujemo
WorldPositionOffset = lerp(POM_Offset, 0, POMFade);
U UE5 Material Editor-u, ovo se implementira koriscenjem Distance Fade node-a koji kontrolise Static Switch ili blenduje izmedju POM i flat verzije materijala.
Tipicni pragovi:
- POM aktivan: 0-5 metara
- Fade out: 5-8 metara
- Flat normal map: 8+ metara
Detail Normal Maps Gde Nisu Vidljive
Detail normal map-e dodaju fine povrsinske detalje (pore koze, zrnatost kamena, teksturu drveta). Ovo zahteva dodatni texture sample i blending matematiku.
Pravilo: Detail normal map-e su korisne samo do ~3-5 metara od kamere. Na vecoj udaljenosti, detalji su manji od piksela i ne doprinose vizuelnom kvalitetu — ali i dalje kostaju GPU cikluse.
// Distance-based detail normal fade
float DetailFade = 1.0 - saturate((CameraDistance - 3.0) / 2.0);
float3 FinalNormal = lerp(BaseNormal, BlendNormals(BaseNormal, DetailNormal), DetailFade);
Nepotrebni Kanali i Slojevi
Pregledajte svaki materijal i postavite sledeca pitanja:
- Da li ovaj materijal zaista treba Emissive kanal? Ako nista ne svetli, iskljucite ga.
- Da li je Ambient Occlusion tekstura zaista neophodna, ili je ambijentalna okluzija vec dovoljno pokrivena kroz SSAO/GTAO/Lumen?
- Da li koristite World Position Offset za animaciju? Ako ne, iskljucite ga.
- Da li je Subsurface zaista potreban na ovom materijalu, ili je obican opaque dovoljan?
- Imate li Custom Data pinove koji nista ne rade?
Svaki ukljucen ali neiskoriscen pin u materijalu potencijalno generise nepotrebne instrukcije, jer UE5 kompajler ne moze uvek da optimizuje dead code u svim slucajevima.
47.5.2 Pojednostavljivanje Matematike
Shaderi su programi koji se izvrsavaju na GPU-u, i kao i svaki program, mogu se napisati efikasno ili neefikasno. Evo najcescih matematickih optimizacija:
Aproksimacije Umesto Egzaktnih Funkcija
Mnoge matematicke funkcije imaju jeftine aproksimacije koje su vizuelno neraspoznatljive:
Power funkcija (pow):
pow(x, n) je relativno skupa operacija. Za ceste slucajeve:
// Umesto:
pow(x, 2.0) // Skupa pow operacija
// Koristite:
x * x // Jeftinije — samo jedno mnozenje
// Umesto:
pow(x, 4.0)
// Koristite:
float x2 = x * x;
float result = x2 * x2; // Dva mnozenja umesto jednog pow-a
Fresnel aproksimacija:
// Egzaktni Fresnel (Schlick):
float fresnel = pow(1.0 - dot(N, V), 5.0);
// Brza aproksimacija za vizualne efekte:
float NdotV = dot(N, V);
float fresnel_approx = 1.0 - NdotV; // Linearna aproksimacija
// Ili za bolji rezultat:
float fresnel_approx2 = (1.0 - NdotV) * (1.0 - NdotV); // Kvadratna aproksimacija
Normalizacija:
// Puna normalizacija:
float3 normalized = normalize(v); // Zahteva sqrt i division
// Ako vektor vec "skoro" normalizovan (npr. interpolirani normali):
// Mozete preskociti normalizaciju u nekim slucajevima
// ili koristiti rsqrt aproksimaciju
float3 fast_normalized = v * rsqrt(dot(v, v));
Smanjivanje Broja Operacija
Ponekad se ista kalkulacija moze izraziti sa manje operacija:
// Neefikasno:
float result = a * b + a * c + a * d;
// Efikasnije (factor out):
float result = a * (b + c + d);
// Neefikasno:
float result = sin(x) * sin(x) + cos(x) * cos(x);
// Efikasnije (ovo je uvek 1.0):
float result = 1.0;
Predracunavanje Konstantnih Izraza
Ako deo kalkulacije zavisi samo od konstanti ili parametara koji se ne menjaju per-piksel, prebacite ga u vertex shader ili cak u CPU:
// Neefikasno (per-pixel):
float scale = MaterialParameter1 * MaterialParameter2 * 0.5;
float result = TextureSample * scale;
// Efikasnije:
// scale se moze izracunati jednom i proslediti kao jedinstven parametar
// umesto da se racuna za svaki piksel
UE5 Material Editor vec radi neke od ovih optimizacija automatski (constant folding), ali ne sve. Sto eksplicitnije napisete vase izraze, veca je sansa da ce kompajler moci da ih optimizuje.
47.5.3 Smanjivanje Broja Texture Sample-ova
Texture sampling je jedna od najskupljih operacija u shaderu. Svaki sample zahteva pristup memoriji (sto moze biti sporo ako tekstura nije u cache-u), filtriranje (bilinearno, trilinearno, anizotropno), i decompression (za kompresovane formate).
Channel Packing
Channel packing je tehnika gde pakujete vise grayscale mapa u razlicite kanale jedne teksture. Umesto da imate cetiri odvojene teksture (Roughness, Metallic, AO, Height), pakujete ih u RGBA kanale jedne teksture:
// Bez channel packing-a — 4 texture sample-a:
float Roughness = Texture2DSample(RoughnessMap, UV).r;
float Metallic = Texture2DSample(MetallicMap, UV).r;
float AO = Texture2DSample(AOMap, UV).r;
float Height = Texture2DSample(HeightMap, UV).r;
// Sa channel packing-om — 1 texture sample:
float4 Packed = Texture2DSample(PackedMap, UV);
float Roughness = Packed.r;
float Metallic = Packed.g;
float AO = Packed.b;
float Height = Packed.a;
Usteda: 3 texture sample-a! To je ogromna razlika.
Napomene za channel packing:
- Sve maske koje delite kanal moraju koristiti iste UV koordinate
- Koristite nekompresovane formate ili formate koji ne blenduju kanale medjusobno (izbegavajte BC1/DXT1 za packed teksture jer kompresija kvari podatke izmedju kanala)
- BC7 ili BC5 su bolji izbori za packed teksture
- U UE5, kreirajte packed teksture u editoru podesavanjem Compression Settings na Masks (no sRGB) ili koristite alate kao sto su Substance Designer ili custom Python/batch skripte za pakovanje kanala pre importa
Proceduralno Generisani Paterni
Mnogi paterni koji se cesto implementiraju kao teksture mogu se generisati matematicki u shaderu, eliminissuci potrebu za texture sample-om:
// Umesto teksture za grid patern:
// Proceduralni grid:
float2 grid = frac(UV * GridScale);
float gridLine = step(grid.x, LineWidth) + step(grid.y, LineWidth);
gridLine = saturate(gridLine);
// Umesto noise teksture za jednostavan noise:
// Proceduralni gradient noise (jeftinija varijanta):
float noise = frac(sin(dot(UV, float2(12.9898, 78.233))) * 43758.5453);
// Umesto teksture za radijalni gradijent:
float radial = length(UV - 0.5) * 2.0;
Upozorenje: Proceduralni paterni nisu uvek jeftiniji od tekstura. Kompleksan proceduralni noise (Perlin, Simplex, Worley) moze biti skuplji od texture sample-a. Koristite ovu tehniku za jednostavne patterne, a za kompleksne noise-ove razmotrite baking u teksturu.
Smanjivanje Broja Tiling Sample-ova
Ako koristite tiling teksture sa razlicitim skalama, razmotrite da li sve skale zaista doprinose vizuelnom kvalitetu:
// Neefikasno — 3 sample-a za isti efekat:
float3 color1 = Texture2DSample(Tex, UV * 1.0).rgb;
float3 color2 = Texture2DSample(Tex, UV * 3.7).rgb;
float3 color3 = Texture2DSample(Tex, UV * 11.3).rgb;
float3 final = (color1 + color2 + color3) / 3.0;
// Efikasnije — 2 sample-a mogu biti dovoljni:
float3 color1 = Texture2DSample(Tex, UV * 1.0).rgb;
float3 color2 = Texture2DSample(Tex, UV * 5.0).rgb;
float3 final = lerp(color1, color2, 0.5);
Deljenje Texture Sample-ova Izmedju Kanala
Ako vise delova vaseg material graph-a koriste isti texture sample sa istim UV-ovima, UE5 kompajler ce obicno to prepoznati i optimizovati u jedan sample. Ali ako koristite razlicite UV transformacije, svaka varijacija postaje zaseban sample.
Savet: Minimizirajte broj razlicitih UV transformacija. Ako mozete, koristite iste UV-ove za vise tekstura.
47.6 Material Instance Hijerarhija za Efikasnost
Jedan od najmocanijih alata za shader optimizaciju u UE5 je sistem Material Instance-ova. Pravilno koriscen, ovaj sistem vam omogucava da imate vizuelno raznovrsne materijale sa minimalnim shader overhead-om.
47.6.1 Parent Material sa Static Switches
Koncept je sledeci: kreirate jedan Parent Material (master material) koji sadrzi sve feature-ove koje bi vam mogli zatrebati, ali kontrolisane kroz Static Switch Parameters. Zatim kreirate Material Instance-ove koji ukljucuju samo potrebne feature-ove.
// Struktura Parent Material-a:
// Static Switch: "Use Detail Normal"
// True: Sample detail normal, blend sa base normal
// False: Koristi samo base normal
// Static Switch: "Use Parallax"
// True: Izracunaj parallax offset
// False: Koristi standardne UV-ove
// Static Switch: "Use Emissive"
// True: Sample emissive teksturu
// False: Emissive = 0
// Static Switch: "Use Vertex Color Blend"
// True: Blend materijale na osnovu vertex color-a
// False: Koristi jedan set tekstura
Kljucna prednost Static Switch-eva: Oni se evaluiraju u compile time-u, ne u runtime-u. Kada "iskljucite" Static Switch, odgovarajuci kod se potpuno uklanja iz kompajliranog shadera. Material Instance sa iskljucenim POM-om nece imati nijednu POM instrukciju.
Ovo je fundamentalno drugacije od koriscenja Scalar Parameter-a za fade (npr. POM_Strength = 0.0). Scalar parameter i dalje izracunava POM — samo rezultat mnozi sa 0. Static Switch eliminise ceo blok koda.
47.6.2 Hijerarhija Material Instance-ova
UE5 podrzava hijerarhijsko nasledjivanje Material Instance-ova:
M_Master_Surface (Parent Material)
|
+-- MI_Stone_Base (iskljucen POM, iskljucen emissive)
| |
| +-- MI_Stone_Granite (specificne teksture i parametri)
| +-- MI_Stone_Marble (specificne teksture i parametri)
| +-- MI_Stone_Slate (specificne teksture i parametri)
|
+-- MI_Stone_Hero (ukljucen POM, iskljucen emissive)
| |
| +-- MI_Stone_Hero_Floor (POM aktiviran, specificne teksture)
| +-- MI_Stone_Hero_Wall (POM aktiviran, specificne teksture)
|
+-- MI_Metal_Base (iskljucen POM, ukljucen emissive)
|
+-- MI_Metal_SciFi_Panel (emissive aktivan, specificne teksture)
+-- MI_Metal_SciFi_Trim (emissive aktivan, specificne teksture)
Prednosti ovog sistema:
-
Shader varijante — Svaka kombinacija Static Switch-eva generise zasebni shader. MI_Stone_Base ce imati jeftiniji shader od MI_Stone_Hero jer nema POM instrukcije.
-
Lako odrzavanje — Promena u Parent Material-u se propagira na sve instance. Ako nadjete bolji nacin za blending, menjate samo jedan materijal.
-
Konzistentnost — Svi materijali u projektu koriste isti shading model, sto olaksava profilisanje i optimizaciju.
-
Efikasniji shader compile — UE5 deli kompajlirane shadere izmedju instanci koje imaju iste Static Switch vrednosti.
47.6.3 Dynamic vs. Static Parametri
Razumevanje razlike je kriticno:
Static Parameters (Static Switch, Static Component Mask):
- Evaluiraju se u compile time-u
- Menjanje zahteva recompile shadera
- Nulti runtime trosak — kod koji nije aktivan ne postoji u shaderu
- Koristite za: ukljucivanje/iskljucivanje feature-ova, izbor izmedju razlicitih tehnika
Dynamic Parameters (Scalar, Vector, Texture):
- Evaluiraju se u runtime-u
- Mogu se menjati u igri bez recompile-a (animacije, interakcije)
- Mali runtime trosak — parametar se ucitava iz constant buffer-a
- Koristite za: boje, intenzitete, UV scale, teksture
Greska koju cesto vidim: Koriscenje dynamic scalar parametra (npr. UseDetail = 0.0 ili 1.0) sa if statement-om u shaderu za ukljucivanje feature-ova. Ovo je lose jer:
- GPU moze da izvrsi obe grane if-a (depending on branching behavior)
- Cak i ako se jedna grana ne izvrsi, kod za obe grane postoji u shaderu
- Branch instrukcija ima svoj trosak
Uvek koristite Static Switch za feature toggles.
47.6.4 Material Function Library
Za ponovno koriscenje logike izmedju razlicitih Parent materijala, koristite Material Functions:
MF_DistanceFade — Zajednicka logika za distance-based fading
MF_ChannelUnpack — Standardni unpack za packed teksture
MF_DetailNormalBlend — Standardni blend za detail normale
MF_ParallaxOcclusion — Optimizovani POM sa fade-out
MF_FresnelCustom — Optimizovani Fresnel sa aproksimacijom
Material Functions se kompajliraju inline (kao C++ inline funkcije), tako da nema overhead-a od pozivanja funkcije. Ali organizuju vas kod i obezbedjuju konzistentnost.
47.7 Early-Out u Shaderima: Clip i Discard
Masked materijali koriste clip() (HLSL) ili discard operaciju za odbacivanje piksela cija je alpha ispod odredjenog threshold-a. Ovo je moc, ali i potencijalan problem.
47.7.1 Kako Clip/Discard Radi
Kada pixel shader izvrsi clip(alpha - threshold):
- Ako je
alpha - threshold < 0, piksel se odbacuje — ne upisuje se u framebuffer - Ako je
alpha - threshold >= 0, piksel se normalno renderuje
U UE5 materijalima, ovo se kontrolise kroz:
- Blend Mode: Masked
- Opacity Mask input na material node-u
- Opacity Mask Clip Value parameter
47.7.2 Kada Clip Pomaze
Clip/discard pomaze performansama u sledecim situacijama:
1. Smanjivanje overdraw-a za vegetaciju:
Drvece i biljke cesto koriste plane-ove sa masked teksturama umesto detaljne geometrije. Bez clip-a, transparentni delovi bi se renderovali (sa blending-om), sto kosta. Sa clip-om, odbaceni pikseli ne upisuju u depth buffer, sto dozvoljava early-Z rejection za piksele iza njih.
2. Decals sa ostrим ivicama:
Decal materijali koji imaju jasne granice (rupe od metaka, logotipi) profitiraju od clip-a jer se samo relevantni pikseli renderuju.
3. LOD transitions sa dithering-om:
UE5 moze koristiti dithered transition izmedju LOD nivoa, gde clip pattern postepeno otkriva/sakriva geometriju.
47.7.3 Kada Clip Steti
Evo gde mnogi developeri grese — clip/discard ima ozbiljne nedostatke u odredjenim situacijama:
1. Unistava Early-Z Rejection:
Moderni GPU-ovi koriste Early-Z (ili Hi-Z) optimizaciju: pre nego sto pokrenu pixel shader, proveravaju da li je piksel vidljiv na osnovu depth buffer-a. Ako nije vidljiv (iza neceg drugog), pixel shader se preskace.
Ali kada materijal koristi discard, GPU ne moze da zna unapred koji ce pikseli biti odbaceni. Zato mora da pokrene pixel shader za svaki piksel, a tek onda da odluci da li ga zadrzati ili odbaciti. Ovo efektivno iskljucuje Early-Z za taj materijal.
Prakticni uticaj: Masked materijal na velikom objektu u pozadini scene (npr. ograda sa rupama) ce primorati GPU da pokrene pixel shader za svaki piksel, cak i za piksele koji ce biti odbaceni. Ovo moze biti skuplje od renderovanja punog opaque materijala.
2. Quad occupancy problem:
Posto GPU procesira piksele u 2x2 quad-ovima, ako je samo 1 od 4 piksela u quad-u vidljiv (ostala 3 su clip-ovana), GPU i dalje procesira sva 4. Ovo je posebno problematicno za materijale sa mnogo sitnih detalja (npr. lisce sa tankim grancicama) — veliki procenat quad-ova je delimicno popunjen.
3. Interferencija sa depth prepass-om:
UE5 moze koristiti depth prepass za optimizaciju renderovanja. Masked materijali komplikuju ovaj proces jer moraju pokrenuti pixel shader cak i u depth prepass-u (da bi znali koji pikseli da se discard-uju).
47.7.4 Prakticne Preporuke za Masked Materijale
Na osnovu gore navedenog, evo preporuka:
-
Preferirajte Opaque nad Masked kad god je moguce. Ako mozete postici slican vizuelni efekat sa geometrijom umesto sa mask-om, ucinite to.
-
Koristite Masked samo za jasne silhouette-e. Lisce, ograde, rupe — situacije gde je mask zaista neophodan.
-
Izbegavajte Masked na velikim povrsinima. Ogromna ograda sa masked materijalom je skuplja nego sto izgleda.
-
Razmotrite Dithered Opacity umesto pravog Masked-a. Dithered opacity je jeftinija jer ne zahteva
discard— koristi pattern u alpha test-u koji radi sa Early-Z. Mana: izgleda "zrnasto" izbliza. -
Koristite dva LOD-a za masked objekte:
- LOD0 (blizu): Detaljna geometrija, opaque materijal
- LOD1+ (daleko): Jednostavna geometrija, masked materijal
-
Postavite Opacity Mask Clip Value pazljivo. Previsok threshold (npr. 0.9) znaci da vise piksela bude clip-ovano, sto moze pomoci overdraw-u ali pogorsava quad occupancy. Prenizak threshold (npr. 0.1) cini mask skoro nevidljivim. Podrazumevana vrednost od 0.333 je dobar pocetak.
47.8 Quality Switches Po Platformi
UE5 omogucava razlicite quality nivoe za razlicite platforme, sto je esencijalno za cross-platform razvoj.
47.8.1 Quality Switch Node
U Material Editor-u, Quality Switch node vam omogucava da pruzite razlicite implementacije za razlicite quality nivoe:
Quality Switch:
Epic: Pun POM + Detail Normals + Tri-planar mapping
High: Jednostavan POM + Detail Normals
Medium: Samo Detail Normals
Low: Osnovna Normal Map
Svaki quality nivo kompajlira se u zasebni shader. Igra automatski bira odgovarajuci shader na osnovu Scalability Settings koje igrac ili sistem postavi.
47.8.2 Feature Level Switch
Slicno, Feature Level Switch node bira implementaciju na osnovu rendering feature level-a:
Feature Level Switch:
SM6: Puna implementacija sa svim feature-ovima
SM5: Skoro puna implementacija, bez SM6-specificnih feature-ova
ES3.1: Mobilna implementacija — minimalni feature-ovi
Ovo je posebno korisno za projekte koji targetiraju i PC i mobilne platforme.
47.8.3 Platform-Specific Material Instances
Za jos finiju kontrolu, mozete kreirati zasebne Material Instance-ove za razlicite platforme:
MI_Stone_Wall_PC — Sve feature-ove ukljucene
MI_Stone_Wall_Console — POM iskljucen, ostalo isto
MI_Stone_Wall_Mobile — Minimalni feature-ovi, manje tekstura
U Project Settings, mozete konfigurisati koji Material Instance se koristi na kojoj platformi kroz Platform Material Quality podesavanja.
47.8.4 Prakticni Quality Budget Po Platformi
Evo preporucenih budzeta za instruction count po platformi:
| Platforma | Background | Mid-range | Hero | Maximum |
|---|---|---|---|---|
| PC (High-end) | <80 | <150 | <250 | 300 |
| PC (Mid-range) | <60 | <100 | <180 | 220 |
| PS5 / Xbox Series X | <70 | <120 | <200 | 250 |
| PS5 / Xbox Series S | <50 | <90 | <150 | 180 |
| Nintendo Switch | <30 | <60 | <100 | 130 |
| Mobile (High-end) | <25 | <50 | <80 | 100 |
| Mobile (Mid-range) | <20 | <35 | <60 | 75 |
Ovi brojevi su okvirni i zavise od specificnosti vaseg projekta (koliki procenat ekrana pokrivaju odredjeni materijali, koliko je overdraw-a, itd.).
47.8.5 Automatsko Prilagodjavanje u Runtime-u
UE5 ima sistem za automatsko prilagodavanje kvaliteta u runtime-u:
// C++ kod za programsko podesavanje material quality-ja:
Scalability::FQualityLevels QualityLevels;
QualityLevels.MaterialQuality = 2; // 0=Low, 1=Medium, 2=High, 3=Epic
Scalability::SetQualityLevels(QualityLevels);
U Blueprint-u, mozete koristiti Set Material Quality Level node za istu funkcionalnost.
Ovo omogucava dinamicko prilagodavanje kvaliteta materijala na osnovu trenutnih performansi (npr. ako frame rate padne ispod ciljanog, snizi quality level za materijale).
47.9 Shader LOD — Jednostavniji Materijali na Daljini
Shader LOD je koncept koji prosiruje ideju geometrijskog LOD-a na materijale: sto je objekat dalje od kamere, jednostavniji materijal se koristi.
47.9.1 Zasto Shader LOD
Zamisite drvo u vasoj sceni:
- Blizu (0-10m): Igrac vidi svaki detalj kore drveta — POM, detail normals, subsurface scattering na liscu, wind animation
- Srednje (10-50m): Igrac vidi oblik drveta i opstu teksturu — osnovna tekstura, normal map, wind animation
- Daleko (50-200m): Igrac vidi siluetu i boju — flat materijal sa jednom teksturom, bez animacije
- Veoma daleko (200m+): Igrac jedva razlikuje drvo od pozadine — najjednostavniji moguc materijal, mozda billboard
Ako koristite isti skupi materijal za sve udaljenosti, bacate GPU cikluse na detalje koje niko ne vidi.
47.9.2 Implementacija Shader LOD-a u UE5
Postoji vise nacina za implementaciju shader LOD-a:
Metod 1: Material sa Distance-Based Switches
Koristite distance kalkulaciju unutar samog materijala za fade izmedju razlicitih nivoa kompleksnosti:
// Pseudo-kod za material graph:
float Dist = CameraDistance;
if (Dist < NearThreshold) {
// Pun materijal
BaseColor = SampleTexture(DetailColor) * SampleTexture(MacroColor);
Normal = BlendNormals(SampleTexture(BaseNormal), SampleTexture(DetailNormal));
Roughness = SampleTexture(PackedMap).r;
// + POM, AO, Emissive...
} else if (Dist < MidThreshold) {
// Srednji materijal
BaseColor = SampleTexture(MacroColor);
Normal = SampleTexture(BaseNormal);
Roughness = SampleTexture(PackedMap).r;
} else {
// Daleki materijal
BaseColor = SampleTexture(MacroColor); // Mozda sa nizim mip-om
Normal = float3(0, 0, 1); // Flat normal
Roughness = 0.5; // Konstantna vrednost
}
Prednost: Jedan materijal za sve, automatski transition. Mana: Kompajler ne moze uvek da eliminise neaktivne grane u runtime-u (GPU dynamic branching).
Metod 2: Odvojeni Materijali po LOD Nivou
Dodelite razlicite materijale razlicitim LOD nivoima mesh-a:
StaticMesh LOD Setup:
LOD0 (0-20m): M_Tree_Bark_Full (150 instrukcija)
LOD1 (20-50m): M_Tree_Bark_Medium (80 instrukcija)
LOD2 (50-100m): M_Tree_Bark_Simple (35 instrukcija)
LOD3 (100m+): M_Tree_Bark_Minimal (15 instrukcija)
Prednost: Svaki LOD ima optimalan shader bez nepotrebnog koda. Mana: Vise materijala za odrzavanje, potencijalni vizuelni "pop" prilikom LOD tranzicije.
Metod 3: Material Instance Per LOD sa Deljenim Parent-om
Ovo je preporuceni pristup koji kombinuje prednosti oba prethodna metoda:
M_Surface_Master (Parent sa Static Switches)
|
+-- MI_Surface_LOD0 (svi feature-ovi ukljuceni)
+-- MI_Surface_LOD1 (POM iskljucen, detail normals iskljucene)
+-- MI_Surface_LOD2 (samo base color i roughness)
+-- MI_Surface_LOD3 (samo base color, konstantan roughness)
Svaka instanca koristi isti Parent materijal ali sa razlicitim Static Switch kombinacijama, sto generise razlicite (optimizovane) shadere.
47.9.3 Nanite i Material Complexity
Sa Nanite-om u UE5, geometrijski LOD je automatizovan — ali materijalni LOD nije. Nanite mesh koji ima milione trouglova i koristi krajnje jednostavan materijal moze biti brzi od mesh-a sa hiljadu trouglova i kompleksnim materijalom.
Vazna napomena za Nanite projekte: Posto Nanite automatski upravlja geometrijskim detaljima, material complexity postaje dominantan faktor u performansama. U Nanite scenama, shader optimizacija je jos vaznija nego u tradicionalnim scenama.
47.9.4 World Partition i Material Streaming
U velikim open world scenama sa World Partition-om, razmotrite:
- Daleki objekti (u outer cells) treba da koriste najjednostavnije materijale
- HLOD (Hierarchical LOD) materijali treba da budu ekstra optimizovani jer pokrivaju velike povrsine
- Imposter materijali za veoma udaljene objekte — jedan quad sa baked osvetljenjem
47.10 Per-Platform Instruction Budgets — Detaljan Pregled
Upravljanje instruction budget-om je disciplina koja zahteva razumevanje specificnosti svake ciljne platforme.
47.10.1 Razumevanje GPU Arhitektura
Razlicite GPU arhitekture imaju razlicite jake i slabe strane:
NVIDIA (PC — Ada Lovelace, Ampere, Turing):
- Odlicne u aritmetici — instruction throughput je visok
- Dobar texture cache — texture sampling je relativno jeftin
- Dynamic branching je efikasan za koherentne uslove
- Instruction budget: mozete priustiti vise aritmetike, budite pazljivi sa texture bandwidth-om
AMD (PC — RDNA 3, RDNA 2; PS5; Xbox Series):
- Drugaciji balans izmedju ALU i texture performance-a
- Wave-based execution — branching je efektivan samo ako je koherentan u okviru wave-a (obicno 32 ili 64 thread-a)
- Na konzolama (PS5/Xbox), imate fiksiran GPU sa poznatim karakteristikama
- Instruction budget: slican PC-u, ali sa vecim naglaskom na konzistentnost
Apple (iOS — A-series, M-series GPU):
- Tile-based deferred rendering (TBDR) arhitektura
- Veoma efikasan za opaque materijale
- Transparencija i overdraw su posebno skupi
- Instruction budget: nizi nego PC, ali TBDR arhitektura pomaze sa overdraw-om
ARM Mali / Qualcomm Adreno (Android):
- Takodje TBDR (Mali) ili slicne arhitekture (Adreno)
- Ogranicen bandwidth i shader performance
- Texture sample-ovi su posebno skupi
- Instruction budget: najnizi od svih platformi
47.10.2 Odredjivanje Vaseg Budzeta
Postupak za odredjivanje realnog instruction budzeta za vas projekat:
Korak 1: Definisanje ciljnog frame rate-a i rezolucije
Cilj: 60 FPS na 1080p
Raspolozivo vreme po frejmu: 16.67ms
GPU render time cilj: ~12ms (ostavljamo marginu za CPU, present, itd.)
Korak 2: Profilisanje na ciljnom hardware-u
Koristite RenderDoc, Nsight, Radeon GPU Profiler, ili UE5 ugradeni profiler da izmerite koliko vremena GPU trosi na pixel shading za vas tip scene.
Primer profilisanja rezultata:
Total GPU time: 11.2ms
Base Pass (pixel shading): 4.8ms (43% ukupnog vremena)
Shadow Depths: 1.9ms
Lighting: 2.1ms
Post Processing: 1.4ms
Other: 1.0ms
Korak 3: Izracunavanje budzeta
Raspolozivo za pixel shading: 4.8ms
Ukupan broj piksela * prosecni overdraw: 2,073,600 * 1.5 = 3,110,400
Raspolozivi ciklusi po pikselu: (4.8ms * GPU_Clock) / 3,110,400
Ovo daje okvirni broj instrukcija koji si mozete priustiti po pikselu. U praksi, cete morati da balansirate izmedju materijala — jednostavniji materijali na velikim povrsinima, kompleksniji na malim.
47.10.3 Budzet Alokacija po Kategoriji Materijala
Preporucena alokacija instruction budzeta:
Ukupni prosecni budzet: 100 instrukcija po pikselu (primer)
Raspodela po kategorijama:
Teren/pod (pokriva ~30% ekrana): 40-60 instrukcija
Zidovi/strukture (pokriva ~25% ekrana): 50-80 instrukcija
Vegetacija (pokriva ~15% ekrana): 60-100 instrukcija
Props (pokriva ~10% ekrana): 70-120 instrukcija
Likovi (pokriva ~10% ekrana): 80-150 instrukcija
Efekti (pokriva ~5% ekrana): 30-80 instrukcija
Sky/pozadina (pokriva ~5% ekrana): 20-40 instrukcija
Kljucni princip: materijali koji pokrivaju vise piksela treba da budu jeftiniji.
47.10.4 Pracenje i Enforcing Budzeta
Uspostavite sistem za pracenje instruction budzeta u timu:
- Naming convention koja ukljucuje complexity tier:
M_T1_Stone_Wall — Tier 1 (do 60 instrukcija)
M_T2_Stone_Wall_Det — Tier 2 (do 120 instrukcija)
M_T3_Character_Skin — Tier 3 (do 200 instrukcija)
-
Automated validation — Napravite editor utility koji proverava instruction count svih materijala u projektu i prijavljuje one koji prekoracuju budzet za svoj tier.
-
Regular profiling sessions — Zakazujte redovne sesije profilisanja na svim ciljnim platformama.
47.11 Praktican Walkthrough: Optimizacija Skupog Materijala
Sada cemo sve nauceno primeniti u praksi. Uzecemo jedan "tezak" materijal i sistematski ga optimizovati.
47.11.1 Pocetni Materijal: M_Stone_Wall_Expensive
Zamislite sledeci materijal za kameni zid u fantasy igri:
Opis:
- Tri sloja tekstura (kamen, malter, mahovina) blendovana vertex color-om
- Svaki sloj ima: Base Color, Normal Map, Roughness, AO (odvojene teksture)
- Parallax Occlusion Mapping za dubinu izmedju kamenih blokova
- Detail Normal Map za mikro-detalje kamena
- Emissive za svetlece rune (runicke linije ugravirane u kamen)
- Proceduralni wind effect na mahovini (World Position Offset)
- Wetness efekat koji menja roughness i boju na osnovu globalnog parametra
Pocetne statistike:
Instruction Count: 312
Texture Samples: 18
Registers Used: 28
Interpolators: 8
Compile Time: 45 sekundi
Ovaj materijal je duboko u beloj zoni Shader Complexity view-a. Na zidu koji pokriva 20% ekrana na 1080p, to je preko 128 miliona instrukcija po frejmu samo za taj jedan zid.
47.11.2 Korak 1: Analiza — Sta je Zaista Potrebno
Pre nego sto pocnemo da secemo feature-ove, moramo razumeti kontekst:
Pitanja za dizajn tim:
- Da li su sva tri sloja vidljiva istovremeno? Odgovor: Ne, vecina zidova koristi samo kamen i malter. Mahovina se pojavljuje na ~20% zidova.
- Da li rune (emissive) svetle konstantno? Odgovor: Da, ali samo na specijalnim zidovima u tamnicama. Vecina zidova nema rune.
- Da li je wetness efekat uvek aktivan? Odgovor: Samo u spoljasnjim scenama kada pada kisa.
- Koliko blizu igrac moze da pridje zidu? Odgovor: Minimalno 0.5m, tipicno 1-3m za koridore.
Zakljucci:
- Mahovina, emissive, i wetness mogu biti opcioni (Static Switch)
- POM je koristan samo izbliza
- Detail normals su korisni samo izbliza
- Mnoge teksture se mogu pakovati
47.11.3 Korak 2: Channel Packing
Prva i najuticajnija optimizacija — pakovanje tekstura:
Pre:
Sloj kamena: BaseColor(1) + Normal(1) + Roughness(1) + AO(1) = 4 sample-a
Sloj maltera: BaseColor(1) + Normal(1) + Roughness(1) + AO(1) = 4 sample-a
Sloj mahovine: BaseColor(1) + Normal(1) + Roughness(1) + AO(1) = 4 sample-a
POM height map: 1 sample
Detail normal: 1 sample
Emissive: 1 sample
Blend mask: 1 sample
Wind noise: 1 sample
Wetness mask: 1 sample
Ukupno: 18 sample-ova
Posle pakovanja:
Sloj kamena: BaseColor(1) + Normal(1) + Packed_R_M_AO_H(1) = 3 sample-a
(Roughness u R, Metallic u G, AO u B, Height u A kanalu)
Sloj maltera: BaseColor(1) + Normal(1) + Packed_R_M_AO(1) = 3 sample-a
Sloj mahovine: BaseColor(1) + Normal(1) + Packed_R_M_AO(1) = 3 sample-a
(ali ovo ce postati opcionalno)
Detail normal: 1 sample (opcionalno)
Emissive + BlendMask: 1 sample (emissive u RGB, blend u A)
Ukupno: 11 sample-ova
Wind noise i wetness mask se mogu generisati proceduralno.
Usteda: 7 texture sample-ova. Ogroman dobitak.
47.11.4 Korak 3: Static Switch Hijerarhija
Pretvaramo materijal u Parent Material sa Static Switches:
Static Switch: "Use Moss Layer"
True: Ukljucuje treci sloj (mahovina) — +3 sample-a, +20 instrukcija
False: Samo kamen i malter
Static Switch: "Use Parallax"
True: POM sa height map-om — +10-30 instrukcija (zavisno od step count-a)
False: Standardne UV koordinate
Static Switch: "Use Detail Normal"
True: Detail normal map + blend — +1 sample, +5 instrukcija
False: Samo base normal
Static Switch: "Use Emissive Runes"
True: Emissive sample i logika — +1 sample, +8 instrukcija
False: Emissive = 0
Static Switch: "Use Wetness"
True: Wetness logika (proceduralna) — +10 instrukcija
False: Standardni roughness
Sada kreiramo instance:
MI_StoneWall_Basic — Samo kamen+malter, bez POM, bez detalja
MI_StoneWall_Detailed — Kamen+malter, sa POM i detail normal
MI_StoneWall_Mossy — Sva tri sloja, bez POM
MI_StoneWall_Mossy_Det — Sva tri sloja, sa POM (hero zidovi)
MI_StoneWall_Runes — Kamen+malter, sa emissive (tamnice)
MI_StoneWall_Wet — Kamen+malter, sa wetness (spoljasniji zidovi)
47.11.5 Korak 4: Matematicke Optimizacije
Detaljni pregled i optimizacija matematickih operacija:
Blend logika (pre):
// Neefikasni blend sa 3 sloja
float3 BlendedColor = Layer1Color * VertexColor.r +
Layer2Color * VertexColor.g +
Layer3Color * VertexColor.b;
// + Height-based blending sa pow i contrast adjustment
float BlendWeight1 = pow(VertexColor.r * HeightMap1, BlendContrast);
float BlendWeight2 = pow(VertexColor.g * HeightMap2, BlendContrast);
float BlendWeight3 = pow(VertexColor.b * HeightMap3, BlendContrast);
float TotalWeight = BlendWeight1 + BlendWeight2 + BlendWeight3;
BlendWeight1 /= TotalWeight;
BlendWeight2 /= TotalWeight;
BlendWeight3 /= TotalWeight;
Blend logika (posle):
// Optimizovani height-based blend
// Koristimo x*x umesto pow(x, 2.0) za BlendContrast=2
float BlendWeight1 = VertexColor.r * HeightMap1;
BlendWeight1 *= BlendWeight1; // Jeftinije od pow
float BlendWeight2 = VertexColor.g * HeightMap2;
BlendWeight2 *= BlendWeight2;
// Za dva sloja (kad mahovina nije aktivna), ne treba normalizacija
// sa 3 sloja — normalizacija je i dalje potrebna ali koristimo rcp()
float InvTotal = rcp(BlendWeight1 + BlendWeight2); // rcp je brze od division-a
BlendWeight1 *= InvTotal;
// BlendWeight2 = 1.0 - BlendWeight1; // Izvedeno — stedimo jednu operaciju
POM optimizacija:
// Pre: Fiksnih 32 koraka POM
for (int i = 0; i < 32; i++) {
// ... raymarching ...
}
// Posle: Adaptivni broj koraka na osnovu udaljenosti i ugla gledanja
int NumSteps = lerp(32, 4, saturate(CameraDistance / POM_FadeDistance));
// Takodje smanjujemo korake za piksele gledane pod malim uglom
NumSteps = max(4, NumSteps * abs(dot(ViewDir, Normal)));
47.11.6 Korak 5: Distance-Based Feature Fade
Implementiramo postepeno iskljucivanje feature-ova na daljini:
float CamDist = length(CameraPosition - WorldPosition);
// POM fade (0-5m aktivan, 5-8m fade, 8m+ iskljucen)
float POMFade = 1.0 - saturate((CamDist - 5.0) / 3.0);
// Detail normal fade (0-3m aktivan, 3-5m fade, 5m+ iskljucen)
float DetailFade = 1.0 - saturate((CamDist - 3.0) / 2.0);
// Roughness detail fade (0-10m aktivan, 10-15m fade, 15m+ konstantan)
float RoughnessFade = 1.0 - saturate((CamDist - 10.0) / 5.0);
Vazna napomena: Ovi distance fade-ovi su dynamic branch-evi — GPU i dalje moze izvrsavati obe grane. Medjutim, u praksi, ako su svi pikseli u jednom quad-u na istoj strani threshold-a (sto je verovatno za udaljene objekte), GPU moze preskociti neaktivnu granu. Ovo se zove coherent branching i moderni GPU-ovi ga dobro optimizuju.
Za garantovano iskljucivanje, koristite odvojene Material Instance-ove po LOD nivou (Korak 6).
47.11.7 Korak 6: Material LOD
Postavljamo razlicite Material Instance-ove za razlicite LOD nivoe mesh-a:
LOD0 (0-10m): MI_StoneWall_Mossy_Det
POM aktivan, detail normals, sva tri sloja
Instruction count: ~145
LOD1 (10-30m): MI_StoneWall_Mossy
Bez POM, bez detail normals, sva tri sloja
Instruction count: ~95
LOD2 (30-80m): MI_StoneWall_Basic
Bez POM, bez detalja, dva sloja
Instruction count: ~55
LOD3 (80m+): MI_StoneWall_Minimal
Jedan sloj, konstantan roughness, flat normal
Instruction count: ~20
47.11.8 Korak 7: Quality Level Varijante
Za razlicite quality nivoe:
Epic Quality: LOD0 sa punim POM (32 koraka)
High Quality: LOD0 sa smanjenim POM (16 koraka)
Medium Quality: LOD0 bez POM, sa detail normals
Low Quality: LOD0 bez POM, bez detail normals
Ovo se implementira kroz Quality Switch node u parent materijalu.
47.11.9 Rezultat Optimizacije
Hajde da uporedimo pre i posle:
Pre optimizacije:
Jedan materijal za sve:
Instruction Count: 312
Texture Samples: 18
Registers: 28
Posle optimizacije:
| Varijanta | Instructions | Tex Samples | Usteda |
|---|---|---|---|
| LOD0 (Hero, Epic) | 145 | 11 | 54% |
| LOD0 (Hero, High) | 125 | 11 | 60% |
| LOD1 (Medium dist) | 95 | 8 | 70% |
| LOD2 (Far) | 55 | 4 | 82% |
| LOD3 (Very far) | 20 | 1 | 94% |
| Basic (no moss/POM) | 65 | 6 | 79% |
| Runes variant | 80 | 7 | 74% |
Vizuelni kvalitet: Za igraca, razlika je neprimetna. Materijal i dalje izgleda odlicno izbliza, a na daljini — gde igrac ionako ne gleda detalje — koristi jeftiniji shader.
Performance uticaj: Na sceni sa mnogo kamenih zidova (tipicna dungeon scena), ova optimizacija moze ustedeti 2-4ms GPU vremena po frejmu, sto je razlika izmedju 30 i 40+ FPS na mid-range hardware-u.
47.11.10 Verifikacija Rezultata
Posle optimizacije, verifikujemo rezultate:
- Shader Complexity View — Scena treba da bude pretezno zelena sa zutim detaljima
- Quad Overdraw View — Proveravamo da nismo uveli novi overdraw
- Frame time profiling — Merimo stvarno poboljsanje u ms
- Visual comparison — Screenshot poredjenje pre i posle na razlicitim udaljenostima
- Platform testing — Proveravamo na svim ciljnim platformama
47.12 Napredne Tehnike
Za one koji zele da idu korak dalje, evo nekoliko naprednih tehnika shader optimizacije.
47.12.1 Shader Permutation Reduction
Svaka jedinstvena kombinacija Static Switch-eva generise posebnu shader permutaciju. Ako imate 10 Static Switch-eva, teoretski mozete imati 1024 razlicite permutacije. Svaka permutacija zauzima memoriju i produzava compile time.
Strategije za smanjivanje permutacija:
-
Grupisanje povezanih feature-ova — Umesto odvojenih switch-eva za "Use Detail Normal" i "Use Detail Roughness", koristite jedan "Use Detail Layer" koji kontrolise oba.
-
Ogranicavanje validnih kombinacija — Ne dozvoljavajte besmislene kombinacije (npr. POM bez Height Map-e). Dokumentujte validne kombinacije.
-
Koristenje Shared Parameters — Materijali koji dele iste Static Switch vrednosti dele isti kompajlirani shader.
47.12.2 Texture Streaming i Material Performance
Texture streaming indirektno utice na shader performance:
- Texture cache misses — Ako tekstura nije u GPU cache-u (jer je evictovana ili jos nije streamovana), texture sample ce biti mnogo sporiji
- Mip level selection — Nizi mip nivoi (manje teksture) su brzi jer bolje koriste cache
- Streaming pool budget — Ako streaming pool predje budzet, teksture ce biti na nizem mip nivou, sto moze prouzrokovati vizuelne artefakte ali poboljsati shader performance
Preporuka: Postavite streaming pool na odgovarajucu velicinu za svaku platformu i monitorisite texture streaming statistike.
47.12.3 Compute Shader Offloading
Za neke operacije, moze biti efikasnije da prebacite kalkulaciju iz pixel shadera u compute shader koji se izvrsava jednom i rezultat cuva u teksturi:
Primeri:
- Proceduralni noise: Izracunajte jednom u compute shaderu, sacuvajte u Render Target
- Animacijski parametri: Izracunajte per-objekat u compute shaderu
- Svetlece probe: Prebacite custom lighting kalkulacije u compute
Ovo smanjuje per-piksel trosak jer pixel shader samo sample-uje rezultat umesto da ga racuna.
47.12.4 Material Expression Custom Node Optimizacije
UE5 Material Editor ponekad generise suboptimalan HLSL kod. Za kriticne putanje, mozete koristiti Custom node za rucno pisanje HLSL-a:
// Custom node za optimizovani 3-slojni blend:
float3 heights = float3(h1, h2, h3) + float3(vc.r, vc.g, vc.b) * 2.0 - 1.0;
float maxH = max(heights.x, max(heights.y, heights.z)) - BlendSharpness;
float3 weights = max(heights - maxH, 0.0);
weights /= dot(weights, 1.0);
return weights;
Upozorenje: Custom node-ovi zaobilaze neke optimizacije koje Material Editor automatski primenjuje. Koristite ih samo kada ste sigurni da vasa rucna implementacija jeste efikasnija. Uvek merite pre i posle.
47.12.5 Async Compute i Material Rendering
Na modernim GPU-ovima (PS5, Xbox Series, noviji PC GPU-ovi), mozete koristiti async compute za paralelno izvrsavanje nekih shader operacija:
- Dok GPU renderuje opaque materijale, compute shader moze da priprema podatke za transparentne materijale
- Post-processing efekti mogu da rade paralelno sa shadow renderingom
UE5 vec koristi async compute interno, ali razumevanje ovog koncepta pomaze pri dizajniranju materiala koji se dobro uklapaju u async pipeline.
47.13 Ceste Greske i Kako Ih Izbeci
47.13.1 "Nikada Necu Optimizovati Ovo"
Greska: Ostavljanje optimizacije za "kasnije" i akumuliranje skupih materijala tokom razvoja.
Resenje: Uspostavite instruction budzet od pocetka projekta. Pregledajte nove materijale pre nego sto udju u produkciju. Lakse je pisati efikasan materijal od pocetka nego optimizovati kompleksan materijal naknadno.
47.13.2 Premature Optimization
Greska: Trosenje vremena na optimizaciju materijala koji pokriva 0.1% ekrana.
Resenje: Prvo identifikujte koji materijali su zaista problematicni (Shader Complexity View + screen coverage analiza), pa optimizujte samo te. Pravilo: fokusirajte se na materijale koji pokrivaju >5% ekrana i imaju >100 instrukcija.
47.13.3 Optimizacija Bez Merenja
Greska: Menjanje materijala "jer izgleda skupo" bez merenja pre i posle.
Resenje: Uvek merite instruction count i frame time pre optimizacije, izvrisite promenu, pa ponovo merite. Ponekad "optimizacija" moze da pogosa stvari (npr. dodavanje branching logike koja je skuplja od koda koji pokusava da izbegne).
47.13.4 Zaboravljanje na Overdraw
Greska: Optimizacija shader complexity-ja dok se ignorise overdraw.
Resenje: Koristite Shader Complexity with Quad Overdraw view mode. Ponekad je bolje smanjiti overdraw (bolji sorting, manje transparentnih objekata) nego smanjivati instruction count.
47.13.5 Nekonzistentna Kvalitetna Podesavanja
Greska: Razliciti umetnici koriste razlicite pristupe za Quality Switch i LOD materijale.
Resenje: Uspostavite standardni Parent Material i Material Function Library za ceo tim. Dokumentujte konvencije i obezbedite alate za validaciju.
47.13.6 Copy-Paste Materijali
Greska: Kopiranje kompleksnog materijala i modifikovanje samo dela, umesto koriscenja Material Instance sistema.
Resenje: Svaka nova "varijacija" treba da bude Material Instance, ne kopija. Ovo smanjuje odrzavanje, obezbedjuje konzistentnost, i cesto poboljsava performanse kroz deljenje shader permutacija.
47.14 Checklist za Shader Complexity Optimizaciju
Evo prakticne checklist-e koju mozete koristiti za svaki materijal u projektu:
Faza 1: Analiza
- Proveriti materijal u Shader Complexity View — koja je boja?
- Proveriti materijal u Quad Overdraw View — koliki je overdraw?
- Proveriti instruction count u Material Stats panelu
- Proveriti texture sample count
- Odrediti screen coverage — koliki procenat ekrana ovaj materijal tipicno pokriva?
- Izracunati efektivni trosak: instruction_count * average_overdraw * screen_coverage
Faza 2: Planiranje
- Identifikovati feature-ove koji se mogu ukloniti bez vizuelnog uticaja
- Identifikovati feature-ove koji se mogu ukloniti na daljini
- Identifikovati teksture koje se mogu pakovati
- Identifikovati matematicke operacije koje se mogu pojednostaviti
- Definisati LOD strategiju za materijal
Faza 3: Implementacija
- Izvrsiti channel packing za teksture
- Implementirati Static Switch-eve za opcionalne feature-ove
- Implementirati distance-based fade za skuplje feature-ove
- Optimizovati matematicke operacije
- Kreirati Material Instance hijerarhiju
- Kreirati LOD varijante materijala
- Implementirati Quality Switch za razlicite quality nivoe
Faza 4: Verifikacija
- Proveriti instruction count posle optimizacije — da li je u okviru budzeta?
- Proveriti vizuelni kvalitet — da li igrac primecuje razliku?
- Proveriti na svim ciljnim platformama
- Proveriti LOD tranzicije — da li su glatke?
- Izmeriti frame time poboljsanje na reprezentativnoj sceni
- Dokumentovati rezultate za buducu referencu
47.15 Integracija sa Drugim Sistemima
Shader complexity optimizacija ne postoji u vakuumu — ona interaguje sa mnogim drugim sistemima u UE5.
47.15.1 Lumen i Material Complexity
Lumen Global Illumination i Reflections sistem u UE5 ima sopstvene zahteve za materijale:
- Lumen Surface Cache koristi pojednostavljene verzije materijala za GI izracunavanja. Kompleksniji materijali zahtevaju vise resursa za surface cache.
- Lumen Reflections renderuje scene ponovo iz perspektive refleksija — sto znaci da se skupi materijali renderuju dodatne puteve.
- Razmotrite uproscavanje materijala za Lumen trace passes koriscenjem odgovarajucih podesavanja u Material Editor-u.
47.15.2 Nanite i Material Complexity
Kao sto smo ranije pomenuli:
- Nanite automatizuje geometrijski LOD
- Material complexity postaje dominantan faktor performansi
- Nanite materijali moraju biti opaque (bez transparency ili masked blend mode-a u standardnom Nanite modu)
- Programable rasterization za Nanite (UE 5.1+) omogucava masked materijale ali sa performansnim troskom
47.15.3 Virtual Shadow Maps
Virtual Shadow Maps u UE5 renderuju shadove sa materijalima koji koriste clip/discard (masked). Kompleksni masked materijali su posebno skupi za shadow rendering jer se pixel shader mora pokrenuti za svaki piksel u shadow map-i.
Preporuka: Za shadow pass, razmotrite koristenje jednostavnijeg materijala ili iskljucivanje nepotrebnih feature-ova u shadow pass-u.
47.15.4 TSR (Temporal Super Resolution)
TSR koristi informacije iz prethodnih frejmova za rekonstrukciju slike na visoj rezoluciji. Materijali sa brzim animacijama ili dithering paternima mogu da zbune TSR i prouzrokuju ghosting artefakte.
Preporuka: Izbegavajte brze animacije u materijalima na velikim povrsinima. Ako koristite dithered opacity, prilagodite pattern za TSR kompatibilnost.
47.16 Rezime Poglavlja
U ovom poglavlju smo prosli kroz kompletan workflow shader complexity optimizacije u UE5:
-
Dijagnostika — Koristimo Shader Complexity View, Quad Overdraw View, i Material Stats za identifikovanje skupih materijala
-
Razumevanje troska — Instruction count, texture samples, overdraw, i screen coverage zajedno odredjuju stvarni uticaj materijala na performanse
-
Strategije redukcije:
- Uklanjanje nepotrebnih feature-ova
- Pojednostavljivanje matematike
- Channel packing za smanjivanje texture sample-ova
- Proceduralni paterni umesto tekstura (gde je prikladno)
-
Sistematska organizacija:
- Material Instance hijerarhija sa Static Switches
- Material Function Library za deljenu logiku
- Quality Switches za per-platform prilagodjavanja
-
Napredne tehnike:
- Early-out sa clip/discard (uz razumevanje trade-off-ova)
- Shader LOD za distance-based simplifikaciju
- Per-platform instruction budzeti
-
Prakticna primena — Demonstrirali smo kako uzeti materijal sa 312 instrukcija i svesti ga na 20-145 instrukcija zavisno od konteksta, bez primetnog gubitka kvaliteta
Kljucna poruka: Shader optimizacija je o pametnim odlukama, ne o zrtvovanju kvaliteta. Igrac ne broji instrukcije — igrac vidi piksele. Nas posao je da postignemo iste piksele sa manje instrukcija.
Kljucni Pojmovi
| Termin | Objasnjenje |
|---|---|
| Shader Complexity | Vizuelni prikaz relativne cene pixel shadera u sceni, prikazan kao heat map od zelene (jeftino) do bele (veoma skupo). |
| Quad Overdraw | Broj puta koliko se isti 2x2 piksel blok (quad) renderuje u jednom frejmu. Vise od 1x znaci da se piksel racuna visestruko. |
| Instruction Count | Broj GPU instrukcija u kompajliranom shaderu. Osnovna metrika za poredjenje kompleksnosti materijala. |
| Channel Packing | Tehnika pakovanja vise grayscale mapa (roughness, metallic, AO, height) u RGBA kanale jedne teksture, smanjujuci broj texture sample-ova. |
| Static Switch | Material parameter koji se evaluira u compile time-u, potpuno uklanjajuci neaktivni kod iz kompajliranog shadera. |
| Material Instance | Instanca Parent materijala koja moze menjati parametre i Static Switch-eve bez kreiranja novog material asset-a. |
| Shader LOD | Koriscenje jednostavnijih materijala na vecim udaljenostima od kamere, analogno geometrijskom LOD-u. |
| Shader Permutation | Jedinstvena varijanta kompajliranog shadera nastala od specificne kombinacije Static Switch vrednosti. |
| Quality Switch | Material node koji pruza razlicite implementacije za razlicite quality nivoe (Low, Medium, High, Epic). |
| Feature Level Switch | Material node koji bira implementaciju na osnovu rendering feature level-a (SM5, SM6, ES3.1). |
| Early-Z (Hi-Z) | GPU optimizacija koja preskace pixel shader za piksele koji su iza vec renderovanih objekata. Masked materijali ometaju ovu optimizaciju. |
| Clip / Discard | Shader operacija koja odbacuje piksel, sprecavajuci njegovo upisivanje u framebuffer. Koristi se za masked materijale. |
| Coherent Branching | Situacija gde svi threadovi u GPU wave/warp grupi uzimaju istu granu — omogucava efikasno preskakanje neaktivne grane. |
| Parallax Occlusion Mapping (POM) | Tehnika za simuliranje dubine na povrsini koriscenjem raymarching-a u texture space-u. Vizuelno impresivna ali skupa. |
| Constant Folding | Kompajlerska optimizacija gde se izrazi koji zavise samo od konstanti izracunavaju u compile time-u umesto runtime-u. |
| Texture Streaming | Sistem za dinamicko ucitavanje i izbacivanje mip nivoa tekstura na osnovu vidljivosti i udaljenosti. |
| Render Target | GPU tekstura u koju se moze renderovati u runtime-u, koristena za baking kalkulacija, refleksije, shadow map-e, i dr. |
| Screen Coverage | Procenat ekrana koji pokriva odredjeni materijal. Kljucni faktor u odredjivanju ukupnog uticaja materijala na performanse. |
Dalje Citanje i Resursi
Zvanicna Unreal Engine Dokumentacija
- UE5 Materials Documentation — Kompletan pregled material sistema
- Material Optimization — Zvanicni vodic za optimizaciju materijala
- Shader Development — Dokumentacija za custom shader razvoj
- Performance Guidelines — Opste smernice za performanse
Unreal Engine Blog i Tech Talks
- "Nanite and Materials" — Epic Games Tech Talk o interakciji Nanite-a i materijala
- "Optimizing Materials for Mobile" — Epic-ov vodic za mobilnu optimizaciju
- "UE5 Rendering Overview" — Detaljan pregled UE5 rendering pipeline-a
GPU Vendor Dokumentacija
- NVIDIA GPU Best Practices — NVIDIA-ine preporuke za shader optimizaciju
- AMD GPUOpen — AMD-ovi alati i smernice za optimizaciju
- ARM Mali Best Practices — Smernice za mobilne GPU-ove
Knjige
- Real-Time Rendering, 4th Edition (Akenine-Moller, Haines, Hoffman) — Biblija real-time renderinga, sa odlicnim poglavljima o shader optimizaciji
- GPU Gems serija (NVIDIA) — Klasicna kolekcija GPU programerskih tehnika
- The Graphics Codex (Morgan McGuire) — Referentni materijal za graficko programiranje
Online Resursi
- Shadertoy — Platforma za eksperimentisanje sa shaderima
- The Book of Shaders — Interaktivni uvod u shader programiranje
- RenderDoc — Besplatan GPU debugger za detaljnu analizu shadera
Povezana Poglavlja u Ovoj Knjizi
- Poglavlje 17: Osnove Shadera — Fundamentalni koncepti shader programiranja, GPU pipeline, vertex i pixel shaderi
- Poglavlje 21: Shader Optimizacija — Teorija — Teorijske osnove shader optimizacije, GPU arhitektura, instruction scheduling
- Poglavlje 22: UE5 Material System — Detaljan pregled UE5 material sistema, Material Editor, node tipovi, material domains
U sledecem poglavlju, preselicemo fokus na optimizaciju osvetljenja i sencenja, gde cemo videti kako Lumen, Virtual Shadow Maps, i nasi optimizovani materijali rade zajedno za postizanje najboljih mogucih performansi.