Poglavlje 47: Shader Complexity Optimizacija

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:

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:

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:

  1. U viewport-u, kliknite na padajuci meni za view mode (podrazumevano pise "Lit")
  2. Idite na Optimization Viewmodes
  3. 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:

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:

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:

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:

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:

Problematicna scena izgleda ovako:

47.2.4 Ogranicenja Shader Complexity View-a

Vazno je razumeti sta ovaj prikaz ne pokazuje:

  1. Ne pokazuje apsolutne brojeve — boje su relativne i zavise od konfiguracije
  2. Ne uzima u obzir screen coverage — materijal moze biti crven ali pokrivati samo 10 piksela, sto je zanemarljivo
  3. Ne pokazuje sve GPU troskove — vertex shader cost, tessellation, compute shaderi nisu prikazani
  4. 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:

47.3.2 Kako Aktivirati Quad Overdraw View

U UE5 editoru:

  1. Kliknite na view mode padajuci meni
  2. Idite na Optimization Viewmodes
  3. Izaberite Quad Overdraw

47.3.3 Citanje Quad Overdraw Prikaza

Quad Overdraw view koristi slican color-coding kao Shader Complexity:

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:

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:

  1. Razlicite instrukcije imaju razlicite troskove — texture sample je obicno mnogo skuplji od aritmeticke operacije
  2. GPU paralelizam — moderni GPU-ovi mogu izvrsavati neke instrukcije paralelno
  3. Cache behavior — texture sample moze biti brz (cache hit) ili spor (cache miss)
  4. 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:

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:

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:

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:

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:

  1. 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.

  2. Lako odrzavanje — Promena u Parent Material-u se propagira na sve instance. Ako nadjete bolji nacin za blending, menjate samo jedan materijal.

  3. Konzistentnost — Svi materijali u projektu koriste isti shading model, sto olaksava profilisanje i optimizaciju.

  4. 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):

Dynamic Parameters (Scalar, Vector, Texture):

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:

  1. GPU moze da izvrsi obe grane if-a (depending on branching behavior)
  2. Cak i ako se jedna grana ne izvrsi, kod za obe grane postoji u shaderu
  3. 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):

U UE5 materijalima, ovo se kontrolise kroz:

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:

  1. Preferirajte Opaque nad Masked kad god je moguce. Ako mozete postici slican vizuelni efekat sa geometrijom umesto sa mask-om, ucinite to.

  2. Koristite Masked samo za jasne silhouette-e. Lisce, ograde, rupe — situacije gde je mask zaista neophodan.

  3. Izbegavajte Masked na velikim povrsinima. Ogromna ograda sa masked materijalom je skuplja nego sto izgleda.

  4. 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.

  5. Koristite dva LOD-a za masked objekte:

    • LOD0 (blizu): Detaljna geometrija, opaque materijal
    • LOD1+ (daleko): Jednostavna geometrija, masked materijal
  6. 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:

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:

  1. Daleki objekti (u outer cells) treba da koriste najjednostavnije materijale
  2. HLOD (Hierarchical LOD) materijali treba da budu ekstra optimizovani jer pokrivaju velike povrsine
  3. 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):

AMD (PC — RDNA 3, RDNA 2; PS5; Xbox Series):

Apple (iOS — A-series, M-series GPU):

ARM Mali / Qualcomm Adreno (Android):

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:

  1. 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)
  1. Automated validation — Napravite editor utility koji proverava instruction count svih materijala u projektu i prijavljuje one koji prekoracuju budzet za svoj tier.

  2. 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:

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:

Zakljucci:

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:

  1. Shader Complexity View — Scena treba da bude pretezno zelena sa zutim detaljima
  2. Quad Overdraw View — Proveravamo da nismo uveli novi overdraw
  3. Frame time profiling — Merimo stvarno poboljsanje u ms
  4. Visual comparison — Screenshot poredjenje pre i posle na razlicitim udaljenostima
  5. 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:

  1. Grupisanje povezanih feature-ova — Umesto odvojenih switch-eva za "Use Detail Normal" i "Use Detail Roughness", koristite jedan "Use Detail Layer" koji kontrolise oba.

  2. Ogranicavanje validnih kombinacija — Ne dozvoljavajte besmislene kombinacije (npr. POM bez Height Map-e). Dokumentujte validne kombinacije.

  3. 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:

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:

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

Faza 2: Planiranje

Faza 3: Implementacija

Faza 4: Verifikacija


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:

47.15.2 Nanite i Material Complexity

Kao sto smo ranije pomenuli:

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:

  1. Dijagnostika — Koristimo Shader Complexity View, Quad Overdraw View, i Material Stats za identifikovanje skupih materijala

  2. Razumevanje troska — Instruction count, texture samples, overdraw, i screen coverage zajedno odredjuju stvarni uticaj materijala na performanse

  3. Strategije redukcije:

    • Uklanjanje nepotrebnih feature-ova
    • Pojednostavljivanje matematike
    • Channel packing za smanjivanje texture sample-ova
    • Proceduralni paterni umesto tekstura (gde je prikladno)
  4. Sistematska organizacija:

    • Material Instance hijerarhija sa Static Switches
    • Material Function Library za deljenu logiku
    • Quality Switches za per-platform prilagodjavanja
  5. Napredne tehnike:

    • Early-out sa clip/discard (uz razumevanje trade-off-ova)
    • Shader LOD za distance-based simplifikaciju
    • Per-platform instruction budzeti
  6. 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

Unreal Engine Blog i Tech Talks

GPU Vendor Dokumentacija

Knjige

Online Resursi

Povezana Poglavlja u Ovoj Knjizi


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.