Poglavlje 20: Proceduralni materijali
Uvod
Zamislite da pravite ogromnu kamenu tvrdjavu u svom nivou. Imate jednu teksturu kamena -- recimo 2048x2048 piksela -- i naneli ste je na zidove. Izgleda pristojno izbliza, ali cim se udaljite ili pogledate veci deo zida, pojavljuje se ocigledna ponavljajuca shema: isti kamen, ista pukotina, ista fleka, iznova i iznova u pravilnoj resetki. To je tiling artifact, jedan od najcescih problema u game art-u. Mozda ste ga vec primetili u prethodnim poglavljima kad smo govorili o teksturama (Poglavlje 05) i UV mapiranju (Poglavlje 04).
A sada zamislite drugaciji pristup: umesto da koristite sliku kamena, vi opisete kamen matematicki. Kazete materijalu: "Napravi Voronoi pattern za oblik kamenih blokova, dodaj Perlin noise za varijacije u boji, pojacaj ivice sa gradient funkcijom, i pospi sve sitnim detaljima koristeci fraktalni sum." Rezultat? Tekstura koja nikad ne ponavljaa isti pattern. Koja izgleda ostra na bilo kojoj udaljenosti. Koju mozete menjati pomocu par parametara -- velicina kamenja, dubina fuga, stepen istrosenosti -- bez ikakvog ponovnog crtanja u Photoshop-u.
To su proceduralni materijali -- i u ovom poglavlju cemo ih rastaviti na delove, razumeti matematiku iza njih, i nauciti kako da ih pravimo u Unreal Engine 5.
Napomena o terminologiji: Kao i u prethodnim poglavljima, koristimo srpski jezik sa latinicnim pismom, dok tehnicke termine ostavljamo na engleskom jeziku jer su tako ustaljeni u industriji. "Noise" ostaje "noise," "shader" ostaje "shader," a "UV" ostaje "UV."
20.1 Sta su proceduralni materijali?
20.1.1 Definicija
Proceduralni materijal (procedural material) je materijal ciji vizuelni sadrzaj nije definisan unapred napravljenim slikama (pre-authored textures), vec se generise algoritmom u realnom vremenu (ili tokom build procesa). Umesto piksela u .png ili .tga fajlu, proceduralni materijal koristi matematicke funkcije, noise algoritme i logicke operacije da stvori boje, normale, roughness i druge podatke.
Zamislite razliku izmedju dva pristupa:
| Aspekt | Tradicionalna tekstura | Proceduralni materijal |
|---|---|---|
| Izvor podataka | Slika (bitmap) | Matematicka funkcija |
| Rezolucija | Fiksna (npr. 2048x2048) | Beskonacna (infinite resolution) |
| Memorija | Zauzima VRAM | Minimalna (samo shader instrukcije) |
| Tiling | Vidljivi pattern-i ponavljanja | Nema ponavljanja (ili kontrolisano) |
| Parametrizacija | Ogranicena (tint, UV scale) | Potpuna kontrola svih aspekata |
| Kreiranje | Rucno u 2D programima | Programiranje/node graf |
| Performanse | Brz texture lookup | Racunski zahtevniji (compute-heavy) |
20.1.2 Zasto "beskonacna rezolucija"?
Kad koristite bitmap teksturu, svaki piksel sadrzi fiksnu informaciju o boji. Kad se kamera priblizi, taj piksel se rasteze -- postaje mutniji. Mogli biste koristiti mipmap-e i anisotropic filtering da ublazite problem (setite se Poglavlja 05), ali sushtinski, informacija je ogranicena.
Proceduralna funkcija, s druge strane, se izracunava za svaki piksel ekrana (ili za svaki fragment, da budemo precizniji). Kad se priblizite, funkcija se evaluira sa preciznijim koordinatama i proizvodi nove detalje. Nema rastezanja, nema mutnoce -- jer ne postoji fiksna slika koja se rasteze. Postoji samo formula koja za svaku tacku u prostoru kaze: "ovde je boja X, roughness Y, normal Z."
Ovo je posebno korisno za:
- Ekstremne close-up kadrove -- zamislite cinematik gde se kamera spusti na centimetar od povrsine
- Velike povrsine -- podovi, zidovi, tereni koji se prostiru na stotine metara
- VR aplikacije -- gde je rezolucija kriticna jer je ekran bukvalno ispred ociju
20.1.3 Parametrizacija: moc jednog slider-a
Mozda je najveca prednost proceduralnih materijala njihova parametrizacija (parameterization). Zamislite da imate materijal za ciglu (brick). Kod bitmap teksture, ako klijent kaze "zelim da cigle budu vece," morate da se vratite u Photoshop, promenite sliku, ponovo eksportujete, ponovo importujete u engine. Ako kaze "zelim da fuge budu tamnije," opet isti proces.
Kod proceduralnog brick materijala, imate parametre:
Brick Width: 0.25 [slider 0.05 - 1.0]
Brick Height: 0.08 [slider 0.02 - 0.5]
Mortar Width: 0.01 [slider 0.001 - 0.05]
Mortar Color: (0.2, 0.2, 0.18) [color picker]
Brick Color A: (0.6, 0.15, 0.1) [color picker]
Brick Color B: (0.7, 0.2, 0.12) [color picker]
Color Variation: 0.15 [slider 0.0 - 1.0]
Roughness Range: 0.6-0.85 [two sliders]
Wear Amount: 0.3 [slider 0.0 - 1.0]
Jedan slider -- i odmah vidite rezultat u viewportu. Nema cekanja na umetnika. Nema ponovnog eksportovanja. Ovo je neverovatno mocno za iteraciju dizajna i za kreiranje varijacija. Od istog proceduralnog materijala mozete napraviti stotine unikatnih zidova.
20.1.4 Kompromisi
Naravno, proceduralni materijali nisu magicno resenje za sve. Postoje realni kompromisi:
Performanse na GPU-u: Svaki piksel zahteva izracunavanje matematickih funkcija umesto prostog citanja iz memorije. Noise funkcije, posebno u vise oktava, mogu biti skupe. Na slabijim GPU-ovima ovo moze biti problem.
Kompleksnost kreiranja: Crtanje teksture u Photoshop-u ili Substance Painter-u je intuitivno -- vidite sta radite. Kreiranje proceduralnog materijala zahteva razumevanje matematike, noise funkcija i shader programiranja. Kriva ucenja je strmija.
Specificni detalji: Proceduralni materijali su odlicni za organske, ponovljive, i apstraktne pattern-e. Ali ako vam treba specifican natpis, logo, ili rucno nacrtan detalj -- bitmap tekstura je jedini put.
Kontrola umetnika: Neki umetnici preferiraju "painterly" pristup gde direktno crtaju svaki detalj. Proceduralni pristup daje manje direktne kontrole nad tacnim izgledom svake tacke.
U praksi, najbolji materijali u modernim igrama koriste hibridni pristup: proceduralnu osnovu za pattern-e i varijacije, kombinovanu sa bitmap teksturama za specificne detalje. Upravo ovo smo poceli da istrazujemo u Poglavlju 19, a sada idemo dublje.
20.1.5 Gde se proceduralni materijali koriste u UE5?
Proceduralne tehnike se u Unreal Engine 5 koriste na vise nacina:
-
Direktno u Material Editor-u -- koristeci noise node-ove, matematicke operacije i pattern generatore koji se izvrsavaju u realnom vremenu na GPU-u. Ovo je fokus ovog poglavlja.
-
Substance Designer/Substance 3D -- eksterni alat specijalizovan za proceduralno generisanje tekstura. Rezultat se moze koristiti kao baked tekstura ili kao Substance plugin u UE5.
-
Houdini i drugi DCC alati -- za proceduralno generisanje geometrije i tekstura koje se zatim eksportuju u engine.
-
Runtime proceduralno generisanje -- za igre koje generisu sadrzaj u realnom vremenu (procedural generation), poput Minecraft-a ili No Man's Sky.
-
Nanite Tessellation (UE5.x) -- gde proceduralni displacement moze da radi na sub-pikselnom nivou zahvaljujuci Nanite tehnologiji.
Mi cemo se fokusirati na prvu tacku -- kreiranje proceduralnih materijala direktno u UE5 Material Editor-u, koristeci node-based sistem koji smo upoznali u Poglavlju 17.
20.2 Noise funkcije -- srce proceduralnih materijala
Ako su proceduralni materijali kuca, onda su noise funkcije (noise functions) njihov temelj, zidovi i krov. Gotovo svaki proceduralni efekat -- od oblaka do mramora, od rdje do pukotina -- koristi neku vrstu noise funkcije kao osnovu.
Ali sta je uopste "noise" u ovom kontekstu? Nije to statik sa TV ekrana (white noise) -- to bi bilo potpuno nasumicno i neupotrebljivo. Proceduralni noise je kontrolisana, glatka pseudo-nasumicnost koja proizovdi pattern-e slicne onima u prirodi.
Hajde da upoznamo glavne tipove.
20.2.1 Value Noise
Value noise je najjednostavniji oblik koherentnog noise-a. Algoritam radi ovako:
- Definisete pravilnu resetku (grid) u prostoru -- 2D, 3D ili koliko god dimenzija zelite.
- Na svakom cvoru resetke dodelite nasumicnu vrednost (obicno izmedju 0 i 1, ili -1 i 1).
- Za tacke izmedju cvorova izracunate vrednost interpolacijom susednih cvorova.
Kljucna rec je "interpolacija." Ako biste samo uzeli najblizi cvor, dobili biste blokove (kao Minecraft teksturu). Umesto toga, koristite glatku interpolacijski funkciju:
Linearna interpolacija (lerp): Najjednostavnija, ali proizvodi vidljive ivice na granicama celija.
lerp(a, b, t) = a + t * (b - a)
Hermite interpolacija (smoothstep): Glatka tranzicija, nema naglih preloma.
smoothstep(t) = t * t * (3 - 2 * t)
Quintic interpolacija (Perlin-ova poboljsana verzija): Jos glatcija, nema diskontinuiteta ni u prvom ni u drugom izvodu.
quintic(t) = t * t * t * (t * (t * 6 - 15) + 10)
Value noise je jednostavan za implementaciju, ali ima jedan znacajan problem: tendenciju da proizvodi "blokast" izgled (blocky appearance). Posto interpoliramo izmedju nasumicnih vrednosti na regularnoj resetki, rezultat cesto ima vidljivu strukturu resetke -- pattern koji se "poravnava" sa osama koordinatnog sistema. Ovo se ponekad naziva "grid-aligned artifacts."
U UE5, value noise mozete koristiti pomocu Noise node-a sa odgovarajucim podesavanjima, mada se u praksi cesce koriste bolji algoritmi.
Kada koristiti Value Noise?
- Kao brzu, jeftinu aproksimaciju kad perfekcija nije potrebna
- Za ucenje koncepata noise-a (najlakse za razumevanje)
- Kad je performanse kriticna i minimalni artifakti su prihvatljivi
20.2.2 Perlin Noise
Perlin noise je mozda najpoznatija noise funkcija u kompjuterskoj grafici. Razvio ga je Ken Perlin 1983. godine dok je radio na filmu Tron. Bio je toliko frustriran vestackim izgledom tadasnje kompjuterske grafike -- sve je izgledalo plasticno i "masinski" -- da je osmislio algoritam koji proizvodi prirodnije, organskije pattern-e. Za ovaj doprinost dobio je Academy Award za tehnicka dostignuca 1997. godine.
Kako radi Perlin Noise?
Kljucna razlika izmedju Perlin noise-a i value noise-a je u tome sta se cuva na cvorovima resetke:
- Value noise: na svakom cvoru je nasumicna vrednost (skalar)
- Perlin noise: na svakom cvoru je nasumicni gradijent (vektor koji pokazuje pravac)
Algoritam za 2D Perlin noise funkcionise ovako:
- Za datu tacku (x, y), odredite u kojoj celiji resetke se tacka nalazi.
- Pronadjite cetiri ugla te celije.
- Za svaki ugao, imate pre-definisan nasumicni gradijentni vektor (gradient vector).
- Izracunajte vektor od ugla do nase tacke (distance vector).
- Izracunajte dot product gradijentnog vektora i distance vektora za svaki ugao. Ovo daje "uticaj" svakog ugla na nasu tacku.
- Interpolirajte cetiri dot product-a koristeci smoothstep ili quintic funkciju.
Rezultat je glatciji i prirodniji od value noise-a jer:
- Noise vrednost je uvek nula na samim cvorovima resetke (posto je distance vector nula), sto eliminise blocky appearance
- Gradijenti obezbedjuju kontinuitet pravca -- prelazi su glatki ne samo u vrednosti vec i u nagibima
- Pattern ne pokazuje ociglednu resetku
Perlin Noise u UE5:
U Material Editor-u, Perlin noise je dostupan kroz Noise node sa Function podesenim na Perlin (ili varijante kao Gradient). Tipicni parametri:
- Scale -- kontrolise frekvenciju noise-a (koliko su "krupni" detalji)
- Levels (Octaves) -- koliko slojeva noise-a se kombinuje (vise o tome u sekciji o FBM)
- Output Min/Max -- remapiranje izlaznog opsega
- Quality -- balans izmedju vizuelnog kvaliteta i performansi
Prakticni savet: Perlin noise sa jednom oktavom izgleda kao meke, zaobljene planine ili oblaci. Sa vise oktava, dobijate detaljnije, fraktalnije pattern-e. Pocnite sa jednom oktavom da razumete osnovu, pa dodajte kompleksnost.
20.2.3 Simplex Noise
Simplex noise je takodje razvio Ken Perlin, 2001. godine, kao poboljsanje svog originalnog algoritma. Zasto mu je bilo potrebno poboljsanje kad je vec dobio Oskar za originalni?
Zato sto Perlin noise ima nekoliko slabosti:
-
Dimenzionalna skalabilnost: Perlin noise u 2D koristi cetiri ugla (kvadrat). U 3D koristi osam uglova (kocka). U 4D koristi sesnaest uglova (hiperkocka). Svaka nova dimenzija udvostrucuje broj tacaka za interpolaciju -- to postaje jako skupo.
-
Vizuelni artifakti: Perlin noise u visim dimenzijama pokazuje blage "ose-poravnane" (axis-aligned) vizuelne artifakte koji mogu da remete organsku estetiku.
-
Anizotropija: Perlin noise ne izgleda potpuno isto u svim pravcima -- postoje blage preferencije ka osama koordinatnog sistema.
Simplex noise resava sve ove probleme koristeci drugaciji geometrijski pristup:
Umesto hiperkocke koristi simplex -- najjednostavniji oblik koji ispunjava prostor u datoj dimenziji:
- 2D: umesto kvadrata (4 tacke), koristi trougao (3 tacke) -- simplex u 2D
- 3D: umesto kocke (8 tacaka), koristi tetraedar (4 tacke) -- simplex u 3D
- 4D: umesto hiperkocke (16 tacaka), koristi pentatop (5 tacaka) -- simplex u 4D
- N-D: koristi samo N+1 tacku umesto 2^N
Ovo znaci da je simplex noise:
- Brzi -- manje tacaka za interpolaciju, O(N^2) kompleksnost umesto O(2^N)
- Glatciji -- manje dimenzionalnih artifakata
- Izotropan -- izgleda isto iz svakog pravca
- Kontinualno diferencijabilan -- glatki prelazi u svim izvodima
Vazna napomena o patentima: Ken Perlin je patentirao specificnu implementaciju 3D simplex noise-a (US Patent 6,867,776). Ovaj patent je istekao 2022. godine, sto znaci da je simplex noise sada slobodan za koriscenje. Pre isteka patenta, mnogi programeri su koristili alternativne implementacije kao sto je OpenSimplex noise (koji koristi drugaciju matematiku da izbegne patent). U vreme pisanja ove knjige, patent vise nije prepreka.
Simplex Noise u UE5:
UE5 nudi simplex noise kroz razlicite node varijante. Mozete ga koristiti direktno ili kroz custom HLSL node. Za vecinu prakticnih slucajeva, razlika izmedju Perlin-a i Simplex-a je suptilna na ekranu, ali simplex noise je efikasniji -- posebno ako koristite 3D ili 4D noise (npr. za animirane efekte gde je vreme cetvrta dimenzija).
20.2.4 Worley (Cellular) Noise
Worley noise (takodje poznat kao cellular noise ili Voronoi noise) funkcionise na potpuno drugacijem principu od Perlin-a i Simplex-a. Umesto interpolacije gradijenta, Worley noise se bazira na rastojanju do najblize tacke (feature point).
Algoritam:
- Rasporedite nasumicne tacke (feature points) u prostoru. U praksi se koristi resetka sa jednom ili vise nasumicnih tacaka po celiji.
- Za svaki piksel, pronadjite najblizu feature tacku i izracunajte rastojanje.
- To rastojanje postaje vrednost noise-a za taj piksel.
Rezultat je karakteristican celijski pattern -- izgleda kao:
- Celije zivog tkiva pod mikroskopom
- Suva, ispucala zemlja
- Kameni zid sa nepravilnim blokovima
- Prirodni Voronoi dijagrami (poput zarafa sarki)
- Kora drveta
- Kristalne strukture
Varijacije Worley noise-a:
Mozete koristiti razlicite metrike rastojanja za razlicite efekte:
- F1 (najbliza tacka): Klasican celijski izgled -- tamno u sredini celije, svetlije ka ivicama.
- F2 (druga najbliza tacka): Slicno F1, ali sa istaknutijim ivicama izmedju celija.
- F2 - F1: Proizvodi samo ivice -- sjajan za zice, mrezaste pattern-e, pukotine.
- F1 * F2: Interesantan organsko-kristalni hibrid.
Razlicite metrike rastojanja:
- Euklidska: sqrt(dx^2 + dy^2) -- daje kruzne, organske celije
- Manhattan: |dx| + |dy| -- daje dijamantske oblike
- Chebyshev: max(|dx|, |dy|) -- daje kvadratne celije
- Minkowski: (|dx|^p + |dy|^p)^(1/p) -- generalizovana metrika, p kontrolise oblik
Worley Noise u UE5:
U Material Editor-u, Worley noise je dostupan kroz VoronoiNoise ili CellNoise node-ove, ili kroz Noise node sa odgovarajucim Function podesavanjem. Takodje mozete pristupiti i Voronoi Distance i Voronoi ID informacijama, sto vam daje:
- Distance: Rastojanje do najblize tacke -- za gradijente unutar celija
- Cell ID (boja celije): Jedinstvena boja/vrednost po celiji -- za nasumicne boje kamenja, plocica itd.
- Edge detection: Ivice izmedju celija -- za malter, fuge, pukotine
20.2.5 FBM (Fractal Brownian Motion)
Do sada smo videli pojedinacne noise funkcije. Ali u prirodi, detalji postoje na svim skalama istovremeno. Pogledajte planinu: ima veliku formu (kilometer-ske krivine), srednje detalje (stene, litice), sitne detalje (kamenicicis, zemljiste) i mikroskopske detalje (zrnca peska). Jedna noise funkcija daje samo jednu skalu detalja.
Fractal Brownian Motion (FBM), takodje poznat kao fraktalni noise ili turbulence, resava ovaj problem tako sto kombinuje vise slojeva (oktava) iste noise funkcije na razlicitim frekvencijama i amplitudama.
Algoritam:
result = 0
amplitude = 1.0
frequency = base_frequency
total_amplitude = 0
for each octave (i = 0 to num_octaves - 1):
result += amplitude * noise(position * frequency)
total_amplitude += amplitude
frequency *= lacunarity // obicno 2.0
amplitude *= persistence // obicno 0.5
result /= total_amplitude // normalizacija
Kljucni parametri:
Octaves (Oktave): Broj slojeva noise-a koji se kombinuju. Svaki sloj dodaje detalje na manjoj skali. Tipicne vrednosti su 4-8. Vise oktava = vise detalja, ali i vise racunanja.
- 1 oktava: Glatke, zaobljene forme (brdasto)
- 3 oktave: Srednji detalji (planinsko)
- 6 oktava: Bogati detalji (realisticno)
- 10+ oktava: Preterani detalji (retko potrebno, skupo)
Lacunarity (Lakunarnost): Koliko se frekvencija povecava sa svakom oktavom. Tipicna vrednost je 2.0, sto znaci da svaka sledeca oktava ima duplo vecu frekvenciju (duplo sitnije detalje).
- Lacunarity < 2.0: Oktave se preklapaju vise, rezultat je "meksi"
- Lacunarity = 2.0: Standardno, dobro pokrivanje svih frekvencija
- Lacunarity > 2.0: Praznine izmedju frekvencija, mozda nedostajuci detalji
Persistence (Perzistencija): Koliko svaka sledeca oktava doprinosi ukupnom rezultatu. Tipicna vrednost je 0.5, sto znaci da svaka sledeca oktava ima duplo manju amplitudu.
- Persistence = 0.0: Samo prva oktava, potpuno glatko
- Persistence = 0.5: Standardno, prirodan izgled
- Persistence = 1.0: Sve oktave podjednako doprinose -- veoma grub, haotican rezultat
Vizualno objasnjenje FBM-a:
Zamislite da crtate planinu:
Oktava 1 (f=1, a=1.0): /\ ___/\___
/ \ / \
/ \__/ \___
Oktava 2 (f=2, a=0.5): /\/\ /\/\ /\/\ /\/\
Oktava 3 (f=4, a=0.25): /\/\/\/\/\/\/\/\/\/\/\/\
Zbir: Realisticna planina sa
krupnim i sitnim detaljima
FBM u UE5:
U Material Editor-u, FBM je dostupan na dva nacina:
-
Noise node sa Levels > 1 -- node automatski kombinuje vise oktava sa podesivim Lacunarity i Persistence parametrima.
-
Rucno slaganje -- koristeci vise Noise node-ova sa rastucim Scale vrednostima i opadajucim amplitudama, sto vam daje potpunu kontrolu nad svakom oktavom (mozete koristiti razlicite noise tipove po oktavi).
20.2.6 Kako noise stvara prirodne pattern-e
Zasto matematicke funkcije proizvode nesto sto izgleda "prirodno"? Kljuc je u tome sto priroda sama funkcionise po fraktalnim principima. Oblaci, planine, reke, drvorede, pukotine na zemlji -- svi oni pokazuju samoslicnost (self-similarity) na razlicitim skalama. FBM tacno replicira ovu osobinu.
Evo kako noise funkcije kreiraju razlicite prirodne efekte:
Oblaci:
- Perlin ili Simplex FBM, 3-5 oktava
- Koristite abs() na noise-u za "billowy" oblik (turbulence varijanta)
- Animirajte pomeranjem noise koordinata tokom vremena
- Remap na kumulativnu distribuciju za realistican oblik
Teren:
- FBM za osnovnu formu
- Worley noise za stenovite detalje
- Erozija simulirana sa domain warping-om (vise o tome kasnije)
- Razliciti noise tipovi za razlicite biome
Vatra i dim:
- Perlin FBM sa visokom persistence (buran, haotican)
- Animirano pomeranjem UV koordinata nagore (vatra se dize)
- Koristite power/contrast da pojacate svetle "jezike"
- 3D noise prolazi kroz volumen za volumetricki dim
Voda:
- Dva sloja Perlin noise-a koji se krecu u razlicitim pravcima
- Gerstner talasi za vece talase (sinusoidne varijacije sa "ostrim" vrhovima)
- Detail noise za sitne talasice
- Normal map generisan iz noise-a za refleksije
Drvo:
- Cilindrican noise -- koristite rastojanje od ose umesto linearnih UV
- Turbulence dodaje nepravilnost godovima
- Worley noise za cvorove
- Elongiran noise u pravcu vlakana
Kamen/Stena:
- Worley noise za strukturu zrna
- FBM za varijacije u boji
- Sharp edge noise za pukotine
- Razliciti slojevi za razlicite tipove kamena
20.3 Proceduralno generisanje tekstura
Sada kad razumemo noise funkcije, hajde da vidimo kako ih kombinujemo sa drugim matematickim operacijama da kreiramo konkretne pattern-e.
20.3.1 Generisanje pattern-a iz matematike
Mnogi korisni pattern-i se mogu izraziti cistom matematikom bez ikakvig noise-a:
Checkerboard (sahovska tabla):
float checker = frac(UV.x * TileCount) > 0.5
^ frac(UV.y * TileCount) > 0.5;
// ^ je XOR operator
// Rezultat: 0 ili 1, naizmenicno
U UE5 Material Editor-u, ovo mozete postici koristeci Checker node ili kombinacijom Floor, Frac i Add node-ova.
Gradijenti:
// Horizontalni gradijent
float horizontal = UV.x;
// Radijalni gradijent
float radial = distance(UV, float2(0.5, 0.5)) * 2.0;
// Dijamantski gradijent
float diamond = abs(UV.x - 0.5) + abs(UV.y - 0.5);
// Kutni (angular) gradijent
float angular = atan2(UV.y - 0.5, UV.x - 0.5) / (2 * PI) + 0.5;
Stripes (linije):
// Horizontalne linije
float stripes = sin(UV.y * Frequency * 2 * PI) > 0 ? 1.0 : 0.0;
// Sa glatkim prelazom (ne ostrim)
float soft_stripes = sin(UV.y * Frequency * 2 * PI) * 0.5 + 0.5;
// Sa kontrolisanom sirinom
float adjustable = smoothstep(0.4, 0.5, frac(UV.y * Frequency));
Brick pattern (cigla pattern):
Ovo je klasican proceduralni pattern koji zasluzuje detaljniji pregled:
// Parametri
float BricksX = 4.0; // broj cigala horizontalno
float BricksY = 8.0; // broj cigala vertikalno
float MortarWidth = 0.05; // sirina fuge
// Korak 1: Skaliraj UV
float2 scaledUV = UV * float2(BricksX, BricksY);
// Korak 2: Odredi red (row)
float row = floor(scaledUV.y);
// Korak 3: Pomeri svaki drugi red za pola cigle
float offset = frac(row * 0.5); // 0 ili 0.5
scaledUV.x += offset;
// Korak 4: Odredi poziciju unutar cigle
float2 brickUV = frac(scaledUV);
// Korak 5: Napravi fuge (mortar)
float mortarX = smoothstep(0.0, MortarWidth, brickUV.x)
* smoothstep(0.0, MortarWidth, 1.0 - brickUV.x);
float mortarY = smoothstep(0.0, MortarWidth, brickUV.y)
* smoothstep(0.0, MortarWidth, 1.0 - brickUV.y);
float mortar = mortarX * mortarY; // 0 = fuga, 1 = cigla
// Korak 6: Dodaj ID cigle za nasumicne boje
float brickID = row * BricksX + floor(scaledUV.x);
U UE5, ovaj pattern mozete izgraditi koristeci:
- UV koordinate kao polaznu tacku
- Multiply za skaliranje
- Floor i Frac za odredjivanje pozicije u resetki
- Add za offset svakog drugog reda
- Smoothstep za glatke ivice fuga
- Ili koristeci gotov BrickPattern node ako je dostupan kroz plugin
Tiles (plocice):
// Heksagonalne plocice
float2 hexUV = UV * float2(1.0, sqrt(3.0));
float2 hexGrid = floor(hexUV);
float2 hexFrac = frac(hexUV);
// Alternativno: koristite Voronoi sa regularnim rasporedom
// za pravilne sesterokutne celije
20.3.2 Kombinovanje noise-a sa pattern-ima
Cisti matematicki pattern-i izgledaju vestacki -- previse pravilni, previse savrseni. Priroda nikad nije savrsena. Kombinovanjem noise-a sa pattern-ima dobijamo organski, uverljiv izgled.
Tehnike kombinovanja:
Noise kao varijacija boje:
Umesto jedne boje za cigle, koristite noise za blagu varijaciju:
float3 baseBrickColor = float3(0.6, 0.15, 0.1);
float colorNoise = PerlinNoise(UV * 10.0) * 0.15; // +/- 15%
float3 variedColor = baseBrickColor + colorNoise;
Noise kao modulacija roughness-a:
float baseRoughness = 0.65;
float roughnessNoise = PerlinNoise(UV * 20.0) * 0.2;
float finalRoughness = saturate(baseRoughness + roughnessNoise);
Noise za deformaciju ivica:
Umesto savrseno pravih fuga izmedju cigala, koristite noise da ih iskrivite:
// Dodaj noise na poziciju pre provere fuge
float edgeNoise = PerlinNoise(UV * 50.0) * 0.01;
float2 distortedUV = scaledUV + edgeNoise;
// Sad racunaj fuge sa distortedUV
Noise za aging/weathering:
float wearNoise = FBM(UV * 8.0, 4); // 4 oktave
float wearMask = smoothstep(WearThreshold - 0.1, WearThreshold + 0.1, wearNoise);
// wearMask = 1 gde je istrosenost, 0 gde je cisto
// Koristite za blend izmedju ciste cigle i prljavog kamena
20.3.3 Domain Warping
Domain warping je jedna od najvaznijih i najlepsih tehnika u proceduralnom generisanju. Koncept je elegantno jednostavan: koristite izlaz jedne noise funkcije da deformisete ulazne koordinate druge noise funkcije.
// Osnovni domain warping
float2 warpedUV = UV + float2(
PerlinNoise(UV * warpScale),
PerlinNoise(UV * warpScale + 100.0) // offset da X i Y budu nezavisni
) * warpAmount;
float result = PerlinNoise(warpedUV * detailScale);
Efekat je zapanjujuci -- dobijate organske, tecne, zakrivljene pattern-e koji podsecaju na:
- Mramor (marble) -- verovatno najpoznatija primena domain warping-a
- Oblake vidjene odozgo -- vrtlozi i kovitlaci
- Agat i druge minerale -- slojevi koji se medjusobno deformisu
- Topografske mape -- izohipse koje se savijaju
- Psihodelicne pattern-e -- za stilizovane umetnicke efekte
Viseslojni domain warping:
Za jos dramaticnije efekte, mozete primeniti domain warping u vise koraka:
// Korak 1: Prvi sloj warping-a
float2 warp1 = float2(
FBM(UV + float2(0.0, 0.0)),
FBM(UV + float2(5.2, 1.3))
);
// Korak 2: Drugi sloj warping-a (warp the warp!)
float2 warp2 = float2(
FBM(UV + 4.0 * warp1 + float2(1.7, 9.2)),
FBM(UV + 4.0 * warp1 + float2(8.3, 2.8))
);
// Finalni rezultat
float result = FBM(UV + 4.0 * warp2);
Ovaj "warping warped noise" pristup je popularizan kroz rad Inigo Quilez-a, jednog od najuticajnijih ljudi u oblasti proceduralnog generisanja. Njegov clanak "Warping by FBM" (dostupan na iquilezles.org) je obavezno stivo za svakoga ko zeli da dublje razume ovu temu.
Domain Warping u UE5 Material Editor-u:
Implementacija je direktna:
- Kreirajte Noise node za warp (npr. Perlin, Scale = 2.0)
- Append dve noise vrednosti u float2 vektor (jedna za X offset, jedna za Y offset)
- Multiply sa faktorom jacine (WarpAmount, npr. 0.1 - 0.5)
- Add rezultat na originalne UV koordinate
- Koristite ove deformisane koordinate kao ulaz za drugi Noise node
Eksperimentisanje je kljucno -- male promene u parametrima mogu dramaticno promeniti izgled.
Animiran domain warping:
Ako na warp noise dodate Time node (pomnozen malim faktorom), dobijate hipnoticne animirane efekte -- tecni mramor, magicne energije, lava tokove. Ovo se koristi za:
- Magicne efekte u RPG igrama
- Sci-fi energetske stene i portale
- Stilizovane voda/lava materijale
- Tranzicione efekte u UI
20.4 Tiling i anti-tiling tehnike
Jedan od vecitih problema u game art-u je tiling -- ponavljanje teksture preko velike povrsine. Proceduralni materijali nude elegantna resenja za oba aspekta ovog problema: kako osigurati da noise ispravno tile-uje, i kako eliminisati vidljivo ponavljanje kod bitmap tekstura.
20.4.1 Zasto proceduralni noise tile-uje prirodno
Vecina noise implementacija je inherentno periodalna (periodic) -- noise funkcija se ponavlja nakon odredjenog perioda. Ovo je zapravo pozeljno, jer znaci da mozete primeniti noise na veliku povrsinu bez da brinete o seamovima.
Perlin noise, na primer, koristi hash funkciju koja mapira celobrojne koordinate resetke na nasumicne gradijente. Posto hash funkcija daje iste rezultate za iste ulaze, noise na poziciji (0, 0) je isti kao noise na poziciji (256, 0) ako je period 256. Ovo znaci da se noise prirodno tile-uje na tim granicama.
Za slucajeve gde vam treba eksplicitna kontrola nad periodom tiling-a, mozete koristiti tileable noise varijante:
// Tileable Perlin noise - koristi modulo operaciju na grid koordinatama
float tileableNoise(float2 pos, float2 period) {
// Grid koordinate se "wrappaju" na period
float2 gridPos = fmod(floor(pos), period);
// Ostatak algoritma je isti kao obican Perlin noise
// ali svaki grid lookup koristi wrapped koordinate
}
U UE5, mozete koristiti frac() i modulo operacije na noise koordinatama da kontrolisete period tiling-a. Noise node takodje ima opciju za Tiling koja eksplicitno kontrolise ponavljanje.
20.4.2 Anti-tiling za bitmap teksture
Dok se proceduralni noise tile-uje "beskonacno" bez vidljivih seam-ova, bitmap teksture imaju ocigledne probleme sa tiling-om. Cak i kad tekstura ima savrsene seamove (leva ivica se spaja sa desnom, gornja sa donjom), ponavljanje istog pattern-a je ocigledno ljudskom oku.
Postoji nekoliko tehnika za borbu protiv ovog problema:
Random offset, rotation, scale per tile:
Ideja: umesto da uvek prikaze istu teksturu na isti nacin, svaka "instanca" dobija nasumicnu transformaciju.
// Za svaku celiju u tiling gridu:
float2 cellID = floor(UV * TileCount);
float randomSeed = hash(cellID); // Nasumican broj baziran na celiji
// Nasumicna rotacija
float angle = randomSeed * 2.0 * PI;
float2 rotatedUV = rotate(frac(UV * TileCount) - 0.5, angle) + 0.5;
// Nasumicni offset
float2 offset = float2(hash(cellID + 1.0), hash(cellID + 2.0));
float2 offsetUV = frac(UV * TileCount + offset);
// Nasumicni scale (blaga varijacija)
float scale = 0.9 + hash(cellID + 3.0) * 0.2; // 0.9 do 1.1
float2 scaledUV = (frac(UV * TileCount) - 0.5) * scale + 0.5;
Problem sa ovim pristupom: na granicama celija se pojavljuju vidljivi seamovi jer se susedne celije rotiraju/skaliraju razlicito.
20.4.3 Stochastic Sampling / Hex Tiling
Stochastic sampling (stohasticko uzorkovanje) je elegantnija tehnika koja resava problem seamova koristeci preklapajuce trougaone ili heksagonalne regione umesto pravougaonih celija.
Hex tiling radi ovako:
- Podelite povrsinu na heksagonalnu resetku
- Svaki heksagon dobija nasumicnu transformaciju (rotacija, offset) teksture
- Na granicama heksagona, blendujte izmedju susednih transformacija koristeci glatku tezinsku funkciju
- Rezultat: bez vidljivih seamova, bez ponavljajucih pattern-a
// Pojednostavljen koncept hex tiling-a:
// 1. Odredite u kom heksagonu se piksel nalazi
// 2. Pronadjite 3 najbliza centra heksagona
// 3. Za svaki od 3 centra, uzorkujte teksturu sa nasumicnom transformacijom
// 4. Blendujte 3 uzorka koristeci barycentric weights
float3 hexTile(Texture2D tex, float2 uv, float scale) {
// ... hex grid math ...
float3 sample1 = tex.Sample(uv_transformed_1) * weight1;
float3 sample2 = tex.Sample(uv_transformed_2) * weight2;
float3 sample3 = tex.Sample(uv_transformed_3) * weight3;
return sample1 + sample2 + sample3;
}
Prednosti hex tiling-a:
- Efikasno eliminise vidljive tiling pattern-e
- Radi sa bilo kojom teksturom, ne zahteva specijalno pripremljene teksture
- Relativno jeftin (3 texture sample-a umesto 1, plus malo matematike)
Mane:
- 3x vise texture sample-a nego obican tiling (moze biti problem na mobile-u)
- Moze da "zamuti" veoma specificne detalje teksture
- Ne radi dobro sa svim tipovima tekstura (npr. teksture sa jasnim linearnim pattern-om)
Hex tiling u UE5:
UE5 ne dolazi sa ugradjenim hex tiling node-om, ali ga mozete implementirati:
- Custom node sa HLSL kodom za hex tiling
- Material Function koja enkapsulira logiku
- Razlicite implementacije dostupne na UE Marketplace-u i u community resursima
- Epic-ov Substrate (Strata) sistem u novijim verzijama UE5 polako integrise napredne anti-tiling opcije
20.4.4 Bombing / Scattering tehnike
Texture bombing (ili scattering) je tehnika gde se mala tekstura (element/dekal) nasumicno rasipa po povrsini, kao da "bombardujete" povrsinu tim elementom.
Tipicna upotreba:
- Lisice po zemlji
- Kamenicici na stazi
- Pukotine na betonu
- Zvezdice na nebu
- Cvecice na livadi
Kako radi:
// Texture Bombing - konceptualni pseudokod
float3 bomb(Texture2D element, float2 uv, float density) {
float3 result = float3(0, 0, 0);
// Za svaku celiju u okolini
for(int x = -1; x <= 1; x++) {
for(int y = -1; y <= 1; y++) {
float2 cellID = floor(uv * density) + float2(x, y);
// Nasumicna pozicija elementa unutar celije
float2 elementPos = hash2D(cellID) + cellID;
elementPos /= density;
// Nasumicna rotacija i scale
float angle = hash(cellID + 100.0) * 2.0 * PI;
float scale = 0.5 + hash(cellID + 200.0) * 0.5;
// Transformisi UV relativno na element
float2 localUV = rotate(uv - elementPos, angle) / scale + 0.5;
// Ako smo unutar bounds-a elementa
if(all(localUV > 0.0 && localUV < 1.0)) {
float4 elementSample = element.Sample(localUV);
// Alpha blend
result = lerp(result, elementSample.rgb, elementSample.a);
}
}
}
return result;
}
Praktichni saveti za bombing u UE5:
-
Koristite atlase -- stavite vise varijacija elementa na jednu teksturu i nasumicno birajte sub-regione. Ovo smanjuje monotoniju.
-
Pazite na overdraw -- svaki element zahteva texture sample. Velika gustina moze biti skupa.
-
Height-based blending -- umesto prostog alpha blenda, koristite height informaciju za realisiticniji prelaz (npr. kamenicici "sede" na zemlji umesto da plutaju iznad).
-
Combine sa proceduralnim noise-om -- koristite noise da modulirate gustinu bombing-a po povrsini (vise kamenicica u udubljenjima, manje na glatkim povrsinama).
20.5 World-Aligned teksture
Do sada smo koristili UV koordinate za mapiranje tekstura na povrsine. Kao sto smo naucili u Poglavlju 04, UV-ovi su 2D koordinate "odmotane" sa 3D povrsine objekta, i svaki objekt ima svoj UV prostor.
Ali sta ako zelite da tekstura izgleda kontinualno preko vise objekata? Ili da nemate UV seamove uopste? Ili da dodate detalje na objekte koji nemaju UV-ove (poput BSP geometrije ili runtime generisanih mesh-eva)?
Tu na scenu stupaju world-aligned textures (teksture poravnane sa svetom) -- tehnika gde koristite poziciju u svetu (World Position) umesto UV koordinata za texture mapping.
20.5.1 Osnovna ideja
Umesto:
color = texture.Sample(mesh.UV)
Koristite:
color = texture.Sample(WorldPosition.xy / TextureScale)
WorldPosition je apsolutna pozicija piksela u svetskom prostoru (world space), izrazena u UE5 jedinicama (centimetrima). Deljenjem sa TextureScale kontrolisete koliko je "krupna" tekstura u svetu.
// World-aligned texture na XY ravni
float3 worldPos = GetWorldPosition(); // u centimetrima
float2 worldUV = worldPos.xy / TextureScale; // npr. TextureScale = 100.0 za 1m
float3 color = MyTexture.Sample(worldUV);
20.5.2 Prednosti
Eliminacija UV seamova:
Posto se koordinate baziraju na poziciji u svetu a ne na UV-ovima objekta, ne postoje UV seamovi. Ovo je posebno korisno za:
- Modularnu arhitekturu (zidovi, podovi, stubovi koji treba da se vizuelno spoje)
- Terene i stene
- Proceduralno generisane mesh-eve
Konzistentan scale:
Svaki objekat u sceni dobija teksturu istog scale-a. Cigla na jednom zidu ce biti iste velicine kao cigla na drugom, bez obzira na UV layout objekata.
Nezavisnost od UV-a:
Objekti ne moraju imati UV koordinate uopste. Ovo je velika prednost za:
- BSP/CSG geometriju
- Runtime generisanu geometriju
- Prototipove (grayboxing) gde ne zelite da trositi vreme na UV unwrap
- Voxel-based terene
20.5.3 Triplanar Mapping
Osnovni world-aligned pristup (projekcija sa jedne ose) ima ocigledian problem: sta se desava sa povrsinama koje su okrenute u razlicitim pravcima?
Ako koristite samo XY projekciju, horizontalni pod ce izgledati odlicno, ali vertikalni zid ce imati "razvucenu" teksturu jer se Y koordinata ne menja duz zida.
Triplanar mapping resava ovo koristeci tri projekcije -- po jednu za svaku osu -- i blendujeci ih na osnovu normala povrsine:
float3 TriplanarMapping(Texture2D tex, float3 worldPos, float3 worldNormal,
float scale, float sharpness) {
// Izracunaj tezine na osnovu normala
float3 weights = abs(worldNormal);
// Pojacaj kontrast tezina (sharpness)
weights = pow(weights, sharpness);
// Normalizuj tezine da zbir bude 1
weights /= (weights.x + weights.y + weights.z);
// Uzorkuj teksturu sa tri projekcije
float3 xProj = tex.Sample(worldPos.yz / scale); // povrsine okrenute ka X
float3 yProj = tex.Sample(worldPos.xz / scale); // povrsine okrenute ka Y (pod/plafon)
float3 zProj = tex.Sample(worldPos.xy / scale); // povrsine okrenute ka Z
// Blenduj
return xProj * weights.x + yProj * weights.y + zProj * weights.z;
}
Kako to izgleda:
- Vertikalni zid okrenut ka severu/jugu: dominira Z projekcija (XY ravna)
- Vertikalni zid okrenut ka istoku/zapadu: dominira X projekcija (YZ ravan)
- Horizontalni pod: dominira Y projekcija (XZ ravan)
- Kosi ugao (45 stepeni): blend izmedju dve projekcije
Sharpness parametar:
Kontrolise koliko je ostar prelaz izmedju projekcija:
- Sharpness = 1: Mekan blend, moze izgledati zamuceno na kosim povrsinama
- Sharpness = 4-8: Dobar balans, umereno ostar prelaz
- Sharpness = 20+: Veoma ostar prelaz, skoro kao hard cut izmedju projekcija
Triplanar Mapping u UE5:
UE5 Material Editor nudi WorldAlignedTexture node koji implementira triplanar mapping. Takodje mozete koristiti:
- WorldPosition node za dobijanje pozicije u svetu
- VertexNormalWS ili PixelNormalWS za normale u svetskom prostoru
- Manuelnu implementaciju za vecu kontrolu
Koraci za postavljanje u Material Editor-u:
- Dodajte WorldPosition node
- Podelite sa Constant (scale faktor, npr. 100 za 1m repeat)
- Razdvojite na komponente koristeci ComponentMask ili BreakOutFloat3Components
- Napravite tri TextureSample node-a sa razlicitim UV parovima (XY, XZ, YZ)
- Dodajte VertexNormalWS node
- Koristite Abs i Power za tezine
- Normalizujte tezine (podelite sa zbirom)
- Multiply svaki sample sa odgovarajucom tezinom
- Add sve tri projekcije
Ili, jednostavnije: koristite gotov WorldAlignedTexture ili TriplanarMapping Material Function.
20.5.4 Performanse
World-aligned textures imaju performansne implikacije koje treba razumeti:
Triplanar mapping = 3x texture samples:
Umesto jednog citanja teksture, imate tri. Za materijal sa albedo, normal, roughness i AO mapama, to je 12 texture sample-ova umesto 4. Na starijim GPU-ovima i mobile uredjajima, ovo moze biti znacajno.
Optimizacije:
-
Redukovani triplanar: Za povrsine koje znate da su uglavnom horizontalne (pod), koristite samo 2 projekcije umesto 3.
-
Lazy evaluation: Prvo izracunajte tezine, pa uzorkujte samo projekcije cija je tezina iznad nekog threshold-a. Ovo moze da eliminise 1-2 texture sample-a na vecini piksela.
// Optimizovani triplanar - preskoci projekcije sa malom tezinom
float3 result = float3(0, 0, 0);
if(weights.x > 0.01) result += tex.Sample(worldPos.yz / scale) * weights.x;
if(weights.y > 0.01) result += tex.Sample(worldPos.xz / scale) * weights.y;
if(weights.z > 0.01) result += tex.Sample(worldPos.xy / scale) * weights.z;
Napomena: Dynamicko grananje (branching) na GPU moze biti samo po sebi skupo -- efikasnost zavisi od koherentnosti piksela. Ako su susedni pikseli u istom "granu" (sto je obicno slucaj za triplanar jer se normala sporo menja), branching je efikasan.
-
Nizi mip level: Za world-aligned detalje na udaljenosti, koristite nize rezolucije tekstura.
-
Proceduralnu zamenu: Za jednostavne pattern-e (cigle, beton), razmotrite potpuno proceduralni pristup umesto triplanar texture sampling-a. Proceduralne noise funkcije su cesto brze od tri texture sample-a.
Problemi sa World-Aligned teksturama na pokretnim objektima:
Ako objekat koristi world-aligned teksture i pomera se, tekstura ce "kliziti" po povrsini objekta jer su koordinate vezane za svet, ne za objekat. Ovo je pozeljno za staticne objekte (zidovi, podovi), ali ne za pokretne (vozila, karaktere, predmete).
Resenje: koristite world-aligned samo za staticne objekte, ili prebacite na Object Space poziciju za pokretne objekte:
// Za pokretne objekte - koristite Object Position umesto World Position
float3 objectPos = TransformWorldToObject(WorldPosition);
float2 objectUV = objectPos.xy / TextureScale;
UE5 nudi ObjectPosition node koji daje poziciju relativno na pivot objekta, sto resava problem klizenja.
20.6 Prakticni primeri
Sada cemo primeniti sve sto smo naucili na konkretne primere. Svaki primer ukljucuje konceptualni pregled, matematicku osnovu i uputstva za implementaciju u UE5 Material Editor-u.
20.6.1 Proceduralni zid od cigala
Ovaj primer je klasican "Hello World" proceduralnih materijala. Kombinuje matematicki pattern sa noise-om za uverljiv rezultat.
Cilj: Napraviti materijal za zid od cigala koji:
- Ima podesive dimenzije cigala i fuga
- Ima nasumicnu varijaciju boje po cigli
- Ima proceduralne normale (bump) za fuge
- Ima blagu istrosenost
Korak 1: Brick Pattern
Osnova je matematicki pattern koji smo videli ranije. U Material Editor-u:
-
Dodajte TextureCoordinate node (UV). Ako zelite world-aligned varijaciju, koristite WorldPosition podeljen sa scale faktorom.
-
Multiply sa brojem cigala po osi:
- U axis: BrickCountX (npr. 4)
- V axis: BrickCountY (npr. 8)
-
Za svaki drugi red, dodajte offset od 0.5:
- Uzmite V komponentu, Floor je, Multiply sa 0.5, Frac -- dobijate 0 ili 0.5
- Add na U komponentu
-
Koristite Frac na oba axis-a da dobijete poziciju unutar svake cigle (0-1 opseg).
-
Za fuge koristite Smoothstep: blizu ivica (0 i 1), vrednost pada ka 0 (fuga). U sredini cigle, vrednost je 1.
Korak 2: Brick ID za varijacije
Svaka cigla treba da ima jedinstvenu identifikaciju za nasumicne varijacije.
- Floor UV koordinate pre Frac operacije -- ovo daje celobrojne koordinate celije.
- Ove koordinate prosledite kroz hash/noise funkciju sa veoma niskom frekvencijom -- dobijate jedinstven "ID" za svaku ciglu.
- Taj ID koristite kao seed za:
- Nasumicnu boju (lerp izmedju dva brick color-a)
- Nasumicnu roughness varijaciju
- Nasumicni rotation za detalj teksturu
Korak 3: Color Variation
// Pseudokod za Material Editor logiku:
BrickColor = lerp(BrickColorA, BrickColorB, hash(BrickID))
// Dodatna fine varijacija koristeci noise
ColorNoise = PerlinNoise(UV * DetailScale) * ColorVariation
FinalBrickColor = BrickColor + ColorNoise
// Mortar boja
MortarColor = DarkGray
// Finalna boja
OutputColor = lerp(MortarColor, FinalBrickColor, BrickMask)
Korak 4: Normal Map
Za normalne, koristite gradijent brick mask-a kao height map i konvertujte u normale:
- Brick mask (1 za ciglu, 0 za fugu) vec ima glatke prelaze zahvaljujuci Smoothstep-u.
- Koristite DDX/DDY node-ove (ili NormalFromHeightMap ako je dostupan) da izracunate normale iz ovog height map-a.
- Ili: eksplicitno izracunajte gradijent pomeranjem UV za mali delta i racunajuci razliku.
// Normala iz height mape (proceduralna):
float h = BrickHeight(UV);
float h_dx = BrickHeight(UV + float2(delta, 0));
float h_dy = BrickHeight(UV + float2(0, delta));
float3 normal = normalize(float3(h - h_dx, h - h_dy, delta));
- Dodajte Perlin noise na normale za sitnu teksturu povrsine cigle.
Korak 5: Roughness
BaseRoughness = lerp(MortarRoughness, BrickRoughness, BrickMask)
RoughnessNoise = PerlinNoise(UV * 30.0) * 0.1
FinalRoughness = BaseRoughness + RoughnessNoise
Korak 6: Wear / Aging
- Generisete FBM noise sa 3-4 oktave na srednjoj frekvenciji.
- Koristite Smoothstep sa WearAmount parametrom kao threshold.
- Gde noise prelazi threshold, blendujte ka "istrosenoj" verziji:
- Svetlija boja (isprano)
- Veca roughness
- Ostecenost ivica (umanjite brick mask blizu fuga)
Rezultat: Potpuno proceduralni brick materijal sa 10+ podesljivih parametara, beskonacnom rezolucijom, bez tiling-a, koji renderuje identican izgled na objektima bilo koje velicine.
20.6.2 Proceduralno drvo
Drvena tekstura je odlican primer koristenja cilindricnog noise-a -- noise koji se evaluira u cilindricnom koordinatnom sistemu.
Zasto cilindrican?
Godovi drveta su koncentricni krugovi kad gledate presek. U 3D, to su cilindricne ljuske. Ako secete drvo pod uglom, dobijate eliptican pattern. Ako secete paralelno sa stablom, dobijate paralelne linije. Sve ovo prirodno proizilazi iz cilindricnog koordinatnog sistema.
Algoritam:
// Korak 1: Cilindrican pattern za godove
// Koristimo rastojanje od Y ose (ako je Y os stabla)
float radius = sqrt(worldPos.x * worldPos.x + worldPos.z * worldPos.z);
// Korak 2: Skaliranje za gustinu godova
float rings = radius * RingDensity; // npr. RingDensity = 20
// Korak 3: Sinusoidna funkcija za svetle/tamne godove
float ringPattern = sin(rings * 2 * PI) * 0.5 + 0.5;
// Korak 4: Turbulence za nepravilnost
float turbulence = FBM(worldPos * NoiseScale, 3) * TurbulenceAmount;
float distortedRings = sin((rings + turbulence) * 2 * PI) * 0.5 + 0.5;
// Korak 5: Boja
float3 lightWood = float3(0.8, 0.6, 0.35);
float3 darkWood = float3(0.4, 0.25, 0.1);
float3 woodColor = lerp(darkWood, lightWood, distortedRings);
// Korak 6: Vlakna (grain) - elongiran noise u pravcu stabla
float grain = PerlinNoise(float3(worldPos.x * 100, worldPos.y * 2, worldPos.z * 100));
woodColor += grain * 0.05;
Implementacija u UE5:
- WorldPosition node -- daje poziciju u svetu
- ComponentMask -- izdvojite X i Z komponente
- VectorLength ili Distance -- izracunajte rastojanje od ose (radius)
- Multiply sa gustinom godova
- Noise node sa niskom frekvencijom -- za turbulence
- Add turbulence na radius pre sine
- Sine -- za pattern godova
- Lerp izmedju svetle i tamne boje drveta
Varijacije:
- Borovo drvo: Jasan kontrast godova, veoma pravilni krugovi, svetla boja
- Hrast: Siri godovi, vise varijacije u boji, tamnija boja
- Mahagonij: Veoma tamno, suptilni godovi, bogata crvenkasta boja
- Brezino drvo: Svetlo sa karakteristicnim tamnim "cvorovima" (Worley noise za cvorove)
Kontrola secenja:
Promena orijentacije UV/world koordinata simulira razlicite uglove secenja:
- Presek (cross-section): krugovi
- Tangencijalni rez: paralelne, blago zakrivljene linije
- Radijalni rez: ravne, paralelne linije
20.6.3 Proceduralni mramor
Mramor je mozda najpoznatija primena domain warping-a. Karakteristicne "vene" (veins) u mramoru nastaju geoloski -- minerali koji se deformisu pod pritiskom. Mi to simuliramo koristeci noise koji deformise noise.
Algoritam:
// Korak 1: Osnovni gradijent (pravac "vena")
float basePattern = UV.x; // ili UV.y, ili dijagonala
// Korak 2: Domain warping za deformaciju vena
float warp1 = FBM(UV * WarpScale1, 4) * WarpAmount1;
float warp2 = FBM(UV * WarpScale2 + 50.0, 4) * WarpAmount2;
// Korak 3: Deformisan pattern
float distorted = basePattern + warp1;
// Korak 4: Sine za vene
float veins = sin(distorted * VeinFrequency * 2 * PI);
// Korak 5: Pojacaj vene (ostriji prelaz)
veins = pow(abs(veins), VeinSharpness); // VeinSharpness = 0.3 za siroke, 3.0 za tanke
// Korak 6: Boja
float3 baseMarble = float3(0.95, 0.93, 0.9); // Beli mramor
float3 veinColor = float3(0.2, 0.2, 0.25); // Tamne vene
float3 marbleColor = lerp(veinColor, baseMarble, veins);
// Korak 7: Drugi sloj vena (finiji)
float fineVeins = sin((distorted + warp2) * VeinFrequency * 4.0 * PI);
fineVeins = pow(abs(fineVeins), VeinSharpness * 2.0) * 0.3;
marbleColor = lerp(marbleColor, veinColor * 0.8, (1.0 - fineVeins) * 0.15);
Implementacija u UE5:
- TextureCoordinate ili WorldPosition za pocetne koordinate
- Dva Noise node-a sa razlicitim Scale vrednostima za dva nivoa warping-a
- Multiply noise sa WarpAmount
- Add na originalnu koordinatu (domain warping)
- Sine node na deformisanu koordinatu
- Abs i Power za kontrolu ostrucine vena
- Lerp izmedju bele i tamne boje
Varijacije mramora:
- Carrara mramor: Beo sa suptilnim sivim venama. Nizak WarpAmount, visoka VeinFrequency, visok VeinSharpness.
- Calacatta: Beo sa dramaticnim zlatno-sivim venama. Veci WarpAmount, niza VeinFrequency, srednji VeinSharpness.
- Emperador: Tamno smedj sa svetlim venama. Invertovane boje, srednji warping.
- Verde Guatemala: Tamno zelen sa belim venama. Promenite baznu boju, visok WarpAmount.
- Nero Marquina: Crn sa belim venama. Visok kontrast, umerena frekvencija.
Pro tip: Za fotorealistican mramor, dodajte subsurface scattering (SSS) efekte -- mramor je translucente i svetlost prodire u njega. U UE5, koristite Subsurface shading model i dodajte blagu SSS boju (topla bela ili bledo roza).
20.6.4 Weathering efekti (istrosenost)
Weathering -- vizuelno starenje i habanje materijala -- je kljucan za fotorealizam. Novi, cisti materijali izgledaju vestacki. Pravi svet je pun prasine, rdje, oguljene boje, zakrzljanih ivica i fleka od vode.
Komponente procedural weathering-a:
Edge Wear (habanje ivica):
Ivice objekata se prirodno vise habaju -- tu se ljudi najcesce dodiruju, tu kiša najvise tuce, tu se boja najlakse guli. Za detekciju ivica u shader-u koristimo curvature (zakrivljenost povrsine).
// Curvature iz normala
// DDX/DDY daju promenu normale po ekranskom prostoru
float3 normalDDX = ddx(WorldNormal);
float3 normalDDY = ddy(WorldNormal);
float curvature = length(normalDDX) + length(normalDDY);
// Visoka curvature = ivica
float edgeWear = smoothstep(CurvatureThreshold, CurvatureThreshold + 0.1, curvature);
U UE5, mozete koristiti:
- DDX/DDY node-ove na WorldNormal
- BentNormalCustomOutput za naprednije tehnike
- Baked curvature mape (iz Substance ili xNormal) za tacniji rezultat
- Ambient Occlusion kao aproksimaciju zakrivljenosti
Dirt Accumulation (nakupljanje prljavstine):
Prasina i prljavstina se skupljaju u udubljenjima i na horizontalnim povrsinama. Koristimo:
- Ambient Occlusion za udubljenja
- World Normal Y komponentu za horizontalnost (prasina pada na gore)
- Noise za organske varijacije
// Dirt maska
float upFacing = saturate(WorldNormal.z); // 1 = potpuno horizontalno (gore)
float dirtBase = upFacing * AO; // Horizontalne povrsine + udubljenja
float dirtNoise = FBM(WorldPos * 0.1, 3) * DirtVariation;
float dirtMask = smoothstep(0.3, 0.7, dirtBase + dirtNoise);
// Primena dirt-a
float3 finalColor = lerp(BaseColor, DirtColor, dirtMask * DirtAmount);
float finalRoughness = lerp(BaseRoughness, DirtRoughness, dirtMask * DirtAmount);
Rust (Rdja):
Rdja se tipicno pojavljuje na metalnim povrsinama, narocito gde je boja ostecena i gde se voda zadrzava.
// Rdja se kombinuje iz vise faktora:
float rustBase = 0.0;
// Faktor 1: Edge wear (boja se guli na ivicama)
rustBase += edgeWear * 0.4;
// Faktor 2: Udubljenja gde se voda skuplja
rustBase += (1.0 - AO) * 0.3;
// Faktor 3: Donja strana (voda curi nadole)
rustBase += saturate(-WorldNormal.z) * 0.2;
// Faktor 4: Organska varijacija
float rustNoise = FBM(WorldPos * RustScale, 5);
float rustMask = smoothstep(RustThreshold - 0.15, RustThreshold + 0.05,
rustBase + rustNoise * 0.5);
// Rdja menja sve kanale:
float3 rustColor = float3(0.45, 0.18, 0.07); // Tamno narandzasta
float rustRoughness = 0.85;
float rustMetallic = 0.0; // Rdja nije metal (oksid)
Moisture / Water Stains (fleke od vode):
// Fleke od vode na zidovima
float moistureGradient = 1.0 - saturate(WorldPos.z / MaxMoistureHeight);
float moistureNoise = FBM(WorldPos.xz * 0.05, 4);
float moistureMask = smoothstep(0.3, 0.5, moistureGradient * moistureNoise);
// Vlaga zatamnjuje boju i smanjuje roughness
finalColor *= lerp(1.0, 0.6, moistureMask);
finalRoughness = lerp(finalRoughness, 0.3, moistureMask * 0.5);
Kombinovanje svih weathering efekata:
U praksi, svi ovi efekti se slazu (layer-uju) jedan na drugi:
- Base material -- cist, neistrosten materijal
- Edge wear -- osvetljava/menja ivice
- Dirt -- zatamnjuje udubljenja i gornje povrsine
- Rust/Oxidation -- menja metal
- Moisture -- zatamnjuje donje delove
- Moss/Organic growth -- zelenkasto na vlaznim, zaklonjenm mestima
Svaki efekat ima svoj mask generisan iz kombinacije geometrijskih faktora (curvature, normal direction, AO, height) i proceduralnog noise-a.
Master Weathering parametar:
Elegantno resenje je imati jedan "Age" slider (0-1) koji kontrolise sve efekte:
- Age = 0.0: Potpuno nov material
- Age = 0.3: Blaga istrosenost ivica, minimalna prljavstina
- Age = 0.5: Umerena istrosenost, vidljiva prljavstina, pocetna rdja
- Age = 0.7: Znacajna istrosenost, debela prljavstina, jasna rdja
- Age = 1.0: Maksimalno staro, sve oronulo
Ovo se postize mapiranjem Age parametra na threshold-ove svakog efekta:
float edgeWearAmount = smoothstep(0.0, 0.3, Age);
float dirtAmount = smoothstep(0.1, 0.5, Age);
float rustAmount = smoothstep(0.3, 0.8, Age);
float mossAmount = smoothstep(0.5, 1.0, Age);
20.7 Napredne teme i saveti za optimizaciju
20.7.1 Performansna razmatranja
Proceduralni materijali mogu biti zahtevni za GPU. Evo saveta za odrzavanje performansi:
Merenje troskova:
UE5 Material Editor prikazuje Instruction Count u statusnoj traci. Ovo je gruba aproksimacija troska:
- < 100 instrukcija: Jeftin materijal
- 100-200 instrukcija: Umeren trosak
- 200-400 instrukcija: Skup materijal
- 400+: Veoma skup, razmotrite optimizaciju
Takodje koristite GPU Profiler (Ctrl+Shift+, ili profilegpu komanda) za precizno merenje vremena renderovanja.
Optimizacijske strategije:
-
Smanjite broj oktava FBM-a. Razlika izmedju 6 i 4 oktave je cesto nevidljiva na ekranu, ali ustedite 33% noise racunanja.
-
Bake-ujte proceduralne rezultate u teksture kad je moguce. Ako se materijal ne menja u runtime-u, mozete render-to-texture proceduralni noise i koristiti rezultat kao obicnu teksturu. Ovo vam daje proceduralni kvalitet sa bitmap performansama.
-
LOD za materijale: Koristite Quality Switch ili custom LOD logiku da uprosti materijal na udaljenosti. Blizu kamere: puni proceduralni materijal sa 6 oktava. Daleko: uproscena verzija sa 2 oktave ili cak baked tekstura.
-
Izbegavajte nepotrebne racunice. Ako noise koristite samo za boju a ne za normale, ne racunajte ga dva puta. Sacuvajte rezultat u Local Variable i reuse-ujte.
-
Koristite LUT teksture za skupe matematicke funkcije (sin, cos, pow) ako ih evaluirate na fiksnom opsegu. 256x1 texture lookup je brzi od trigonometrijskih operacija.
-
Shared Material Functions -- enkapsulairajte proceduralne elemente u Material Functions koje mozete ponovo koristiti bez dupliranja koda.
20.7.2 Debugging proceduralnih materijala
Debugging proceduralnih materijala moze biti frustrirajuci -- ne vidite "kood" koji se izvrsava, samo krajnji rezultat. Evo tehnika:
-
Preview svaki node -- desni klik na node u Material Editor-u i izaberite "Start Previewing Node." Ovo prikazuje izlaz tog node-a na previewu materijala. Koristite ovo da korak po korak pratite sta se desava.
-
Vizualizujte medjurezultate -- privremeno spojite medjurezultat (npr. noise mask) na Emissive output da vidite sta noise zapravo proizvodi.
-
Remap na 0-1 opseg -- mnogi noise-ovi izlaze u opsegu -1 do 1. Dodajte 0.5 i pomnozite sa 0.5 da remap-ujete na vidljiv opseg (ili koristite LinearToGamma za bolji vizuelni kontrast).
-
Koristite boje za debug -- maoirajte razlicite kanale na R, G, B da istovremeno vidite tri razlicita medjurezultata.
-
Staticki parametri -- koristite Static Switch node-ove da napravite "debug mode" koji mozete ukljuciti/iskljuciti bez recompile-a.
20.7.3 Hibridni pristup: Proceduralno + Bitmap
U praksi, najupecatljiviji materijali koriste hibridni pristup:
- Proceduralni elementi: Pattern-i (cigle, plocice), varijacije boje, weathering, noise za roughness
- Bitmap elementi: Specificni detalji (logotip, natpis), fotografski referenciran micro-detail, hand-painted elementi
Tipican workflow:
- Kreirajte proceduralnu osnovu u UE5 Material Editor-u (pattern, noise, weathering)
- Dodajte detail teksture iz Substance-a ili Photoshop-a (micro-normal, detail albedo)
- Koristite proceduralne maske da kontrolisete gde se detail teksture primenjuju
- Parametrizujte sve za laku iteraciju
20.7.4 Substance Designer i UE5
Vredi pomenuti Substance 3D Designer -- industrijski standard za proceduralno kreiranje tekstura. Substance koristi node-based sistem slican UE5 Material Editor-u, ali sa fokusom na offline generisanje tekstura.
Kljucna razlika:
- UE5 Material Editor: Proceduralni noise se racuna u realnom vremenu na GPU-u za svaki frame
- Substance Designer: Proceduralni noise se izracuna jednom i rezultat se cuva kao bitmap tekstura
Substance pristup daje neogranicenu kompleksnost (moze koristiti stotine noise slojeva, simulirati eroziju, itd.) bez runtime performansnog troska. UE5 pristup daje beskonacnu rezoluciju i runtime parametrizaciju ali je ogranicen GPU budzetom.
Mnogi profesionalni timovi koriste oba pristupa:
- Substance za kompleksne bazne teksture (albedo, normal, roughness mape)
- UE5 procedurale za runtime varijacije, blending, weathering
20.8 Veza sa drugim poglavljima
Proceduralni materijali se nadovezuju na mnoge koncepte iz prethodnih poglavlja:
-
Poglavlje 04 (UV Mapping): Razumevanje UV koordinata je esencijalno -- proceduralni materijali koriste UV ili World Position kao ulazne koordinate. World-aligned textures su alternativa UV mapiranju.
-
Poglavlje 05 (Teksture): Procedurale teksture su alternativa bitmap teksturama. Razumevanje formata, kompresije i filtriranja iz Poglavlja 05 je relevantno za hibridne materijale.
-
Poglavlje 17 (Shader-i): Material Editor je shader editor -- svaki node se prevodi u HLSL instrukcije. Razumevanje shader pipeline-a pomaze u optimizaciji proceduralnih materijala.
-
Poglavlje 19 (Tehnike tekstura): Mnoge tehnike iz Poglavlja 19 (blending, masking, detail mapping) se koriste u kombinaciji sa proceduralnim elementima.
U narednim poglavljima, videcete kako se proceduralni materijali koriste za:
- Terene (proceduralni landscape materijali)
- Efekte (vatrene, vodene, magicne efekte koristeci proceduralni noise)
- Optimizaciju (baking proceduralnih rezultata za bolje performanse)
20.9 Rezime poglavlja
U ovom poglavlju smo prosli kroz svet proceduralnih materijala -- od osnovnih noise funkcija do prakticnih primera. Hajde da sumiramo kljucne tacke:
-
Proceduralni materijali generisu vizuelni sadrzaj matematicki umesto iz bitmap slika. Nude beskonacnu rezoluciju, odsustvo tiling artifakata i mocan sistem parametrizacije.
-
Noise funkcije su temelj proceduralnih materijala:
- Value noise interpolira nasumicne vrednosti na resetki
- Perlin noise koristi gradijente za glatciji rezultat
- Simplex noise je efikasnija verzija Perlin-a bez dimenzionalnih ogranicenja
- Worley noise koristi rastojanje do najblize tacke za celijske pattern-e
- FBM kombinuje vise oktava za fraktalne detalje na svim skalama
-
Proceduralno generisanje tekstura kombinuje matematicke pattern-e (cigle, plocice) sa noise-om za organske varijacije. Domain warping je kljucna tehnika za prirodne, tecne deformacije.
-
Anti-tiling tehnike (hex tiling, stochastic sampling, texture bombing) eliminisu vidljivo ponavljanje bitmap tekstura.
-
World-aligned textures koriste poziciju u svetu umesto UV koordinata, eliminisujuci UV seamove. Triplanar mapping prosiruje ovo na proizvoljno orijentisane povrsine.
-
Prakticni primeri -- cigla, drvo, mramor i weathering -- demonstriraju kako se ovi koncepti kombinuju u realisticne materijale.
20.10 Tabela kljucnih termina
| Termin (engleski) | Objasnjenje |
|---|---|
| Procedural Material | Materijal ciji se vizuelni sadrzaj generise algoritmom, ne iz unapred napravljenih slika. |
| Noise Function | Matematicka funkcija koja proizvodi kontrolisanu, glatku pseudo-nasumicnost. |
| Value Noise | Noise baziran na interpolaciji nasumicnih vrednosti na regularnoj resetki. |
| Perlin Noise | Noise baziran na gradijentnim vektorima na cvorovima resetke. Razvio Ken Perlin 1983. |
| Simplex Noise | Poboljsana verzija Perlin noise-a, efikasnija u visim dimenzijama. Takodje Ken Perlin, 2001. |
| Worley Noise (Cellular Noise) | Noise baziran na rastojanju do najblizih feature tacaka. Proizvodi celijske/Voronoi pattern-e. |
| FBM (Fractal Brownian Motion) | Tehnika kombinovanja vise oktava noise-a na razlicitim frekvencijama i amplitudama. |
| Octave | Jedan sloj noise-a u FBM procesu. Svaka oktava dodaje detalje na manjoj skali. |
| Lacunarity | Faktor povecanja frekvencije izmedju uzastopnih oktava u FBM-u. Tipicno 2.0. |
| Persistence | Faktor smanjenja amplitude izmedju uzastopnih oktava u FBM-u. Tipicno 0.5. |
| Domain Warping | Tehnika koristenja izlaza jednog noise-a za deformaciju ulaznih koordinata drugog noise-a. |
| Tiling | Ponavljanje teksture/pattern-a preko povrsine. |
| Anti-Tiling | Tehnike za eliminaciju vidljivog ponavljanja u tiling-u (hex tiling, stochastic sampling, itd.). |
| Hex Tiling | Anti-tiling tehnika koja koristi heksagonalnu resetku i blending preklapajucih regiona. |
| Texture Bombing | Tehnika nasumicnog rasipanja malih tekstura/elemenata po povrsini. |
| Stochastic Sampling | Statisticki pristup uzorkovanju koji eliminise periodiku tiling-a. |
| World-Aligned Texture | Textura mapirana koristeci poziciju u svetu umesto UV koordinata objekta. |
| Triplanar Mapping | Varijanta world-aligned texturing-a koja koristi tri ortogonalne projekcije i blenduje ih na osnovu normala. |
| Curvature | Zakrivljenost povrsine, koristi se za detekciju ivica u weathering efektima. |
| Weathering | Vizuelno starenje i habanje materijala (rdja, prljavstina, habanje ivica, vlaga). |
| Baking | Proces renderovanja proceduralnog rezultata u bitmap teksturu za bolje performanse. |
| Material Function | Enkapsulirana logika materijala u UE5 koja se moze ponovo koristiti u vise materijala. |
| Hash Function | Funkcija koja mapira ulaz na pseudo-nasumican izlaz. Koristi se za generisanje seed-ova u noise algoritmima. |
| Smoothstep | Interpolacijska funkcija sa glatkim prelazom (nulti prvi izvod na krajevima). |
20.11 Korisni resursi i dalje citanje
Online resursi:
-
Inigo Quilez -- iquilezles.org: Neprevazidjeni resursi za proceduralne tehnike, noise funkcije, SDF-ove i raymarching. Posebno pregledajte clanke o domain warping-u i noise-u.
-
The Book of Shaders (thebookofshaders.com): Besplatan, interaktivan kurs o shader programiranju sa odlicnim poglavljima o noise-u i proceduralnim pattern-ima.
-
Shadertoy (shadertoy.com): Online platforma za eksperimentisanje sa shader kodom. Hiljade primera proceduralnih materijala. Odlicno za ucenje i inspiraciju.
-
Red Blob Games (redblobgames.com): Interaktivni tutorijali o noise funkcijama, heksagonalnim resetkama i proceduralnom generisanju.
-
Ken Perlin-ova stranica (cs.nyu.edu/~perlin): Originalni radovi i implementacije Perlin i Simplex noise-a od samog tvorca.
Knjige:
-
"Texturing & Modeling: A Procedural Approach" (Ebert, Musgrave, Peachey, Perlin, Worley): Biblija proceduralnog generisanja. Sadrzi poglavlja od samih izumitelja Perlin i Worley noise-a.
-
"Real-Time Rendering" (Akenine-Moller, Haines, Hoffman): Sveobuhvatna referenca za renderovanje u realnom vremenu, ukljucujuci proceduralne tehnike.
-
"GPU Gems" serija (NVIDIA): Besplatno dostupna online, sadrzi mnoge clanke o proceduralnim tehnikama na GPU-u.
UE5 specificni resursi:
-
Unreal Engine dokumentacija -- Materials sekcija: Zvanicna dokumentacija sa primerima koristenja noise node-ova i proceduralnih tehnika.
-
Unreal Engine Learning Portal: Video tutorijali o proceduralnim materijalima u UE5.
-
Ben Cloward YouTube kanal: Odlicni video tutorijali o shader tehnikama u UE5, ukljucujuci proceduralne pattern-e.
-
Ryan Brucks blog/portfolio: Napredno koristenje proceduralnih materijala u UE4/UE5 od jednog od Epic-ovih materijalnih inzenjera.
U sledecem poglavlju cemo istraziti kako se materijali primenjuju na terene (landscapes) i kako proceduralne tehnike iz ovog poglavlja igraju kljucnu ulogu u kreiranju believable okruzenja na velikim skalama.