Poglavlje 14: Ray Tracing
Uvod: Svetlost putuje pravolinijski -- hajde da je pratimo
U dosadasnjim poglavljima smo detaljno obradili rasterizaciju (Poglavlje 09), osvetljenje (Poglavlje 10), PBR (Poglavlje 11), i globalnu iluminaciju (Poglavlje 12). Kroz svaki od tih odeljaka, cesto smo pominali koncepte poput "ispucaj zrak", "testiraj presek sa troouglom", "BVH struktura" -- ali nikada se nismo zaustavili da zaista razumemo kako sve to funkcionise. U ovom poglavlju konacno cemo.
Ray tracing nije nova ideja. Arthur Appel je opisao osnovni koncept 1968. godine, a Turner Whitted je 1980. objavio rad koji je uveo rekurzivno pracenje zraka -- sa refleksijama, refrakcijama, i senkama. Filmska industrija koristi ray tracing vec decenijama za fotorealisticne slike. Ali u igrama? Ray tracing je dugo bio san -- previse skup, previse spor, previse daleko od real-time performansi.
To se promenilo 2018. godine sa NVIDIA-inom Turing arhitekturom i pojavom dedikovanog hardvera za ray tracing. Odjednom, bar neki delovi ray tracing-a postaju mogucni u real-time-u. Ne potpun path tracing (o tome smo govorili u Poglavlju 12), ali hibridni pristup -- rasterizujte ono sto moze brzo, a ray tracing koristite tamo gde daje dramaticno bolje rezultate nego rasterizacija.
Unreal Engine 5 je jedan od pionira ovog hibridnog pristupa. Lumen (Poglavlje 25) moze da koristi hardware ray tracing za preciznije rezultate. RT refleksije, RT senke, RT ambient occlusion -- sve su opcije u UE5 koje oslanjaju na koncepte koje cemo obraditi u ovom poglavlju.
Ali pre nego sto pricamo o UE5, moramo razumeti osnove. Ovo poglavlje pokriva:
- Rasterizacija vs ray tracing -- fundamentalna razlika u pristupu, i zasto oba pristupa postoje
- Osnovni algoritam ray tracing-a -- od kamere, kroz piksel, do trougla
- Rekurzivni ray tracing -- refleksija, refrakcija, senke (Whitted-style)
- BVH (Bounding Volume Hierarchy) -- akceleraciona struktura bez koje ray tracing ne bi bio praktican
- Ray-triangle intersection -- Moller-Trumbore algoritam, matematika koja se izvrsava milijardama puta po frejmu
- RT hardver -- RT Core-ovi, Ray Accelerator-i, i zasto je specijalizovani hardver neophodan
- Hibridno renderovanje -- prakticni pristup koji kombinuje rasterizaciju i ray tracing
- DXR i Vulkan RT pipeline -- kako softverski pipeline izgleda za ray tracing
- Ray tracing u UE5 -- kako Unreal Engine koristi sve ovo u praksi
Ovo je gusto poglavlje sa dosta matematike, ali svaki koncept je direktno povezan sa necim sto cete videti (ili konfigurisati) u Unreal Engine-u. Krenimo od pocetka -- od fundamentalnog pitanja koje razdvaja dva pristupa renderovanju.
14.1 Rasterizacija vs Ray Tracing -- Dva Pogleda na Isti Problem
Fundamentalno pitanje
Renderovanje u svojoj sustini resava jedan problem: za svaki piksel na ekranu, odrediti koju boju treba da ima. Dva fundamentalno razlicita pristupa ovom problemu su rasterizacija i ray tracing.
Rasterizacija pita: "Za svaki trougao u sceni, koje piksele on pokriva?"
Ray tracing pita: "Za svaki piksel na ekranu, koji trougao on vidi?"
Na prvi pogled, ovo izgleda kao ista stvar posmatrana iz dva razlicita ugla. I u izvesnom smislu jeste -- oba pristupa na kraju odredjuju koji trougao je vidljiv za svaki piksel. Ali razlika u pristupu ima ogromne posledice po performanse, mogucnosti, i ogranicenja.
Rasterizacija -- pristup "iznutra ka spolja"
Rasterizacija (koju smo detaljno obradili u Poglavlju 09) radi ovako:
- Uzmite svaki trougao u sceni
- Transformisite ga u screen space (vertex shader)
- Odredite koje piksele pokriva (rasterizacija + edge equations)
- Za svaki pokriveni piksel, izracunajte boju (pixel shader)
- Koristite depth buffer da odredite koji trougao je najblizi kameri
Ovaj pristup je object-order rendering -- iteriramo kroz objekte (trouglove) i odredjujemo njihov uticaj na sliku. Kljucna prednost: svaki trougao se obradi jednom, nezavisno od svih ostalih trouglova. Ovo je fantastican model za paralelizaciju na GPU-u (o cemu smo govorili u Poglavlju 08).
Rasterizacija (object-order):
Za svaki trougao T u sceni:
Za svaki piksel P koji T pokriva:
Ako je T blizi kameri od trenutnog piksela u depth buffer-u:
Odredi boju P na osnovu T-ovog materijala
Zapisi dubinu T u depth buffer
Ray tracing -- pristup "spolja ka unutra"
Ray tracing radi obrnuto:
- Za svaki piksel na ekranu, "ispucajte" zrak (ray) iz kamere kroz taj piksel
- Pronadjite prvi trougao u sceni koji taj zrak pogadja
- Na mestu preseka, izracunajte boju tog piksela
Ovaj pristup je image-order rendering -- iteriramo kroz piksele (sliku) i trazimo sta svaki piksel vidi. Kljucna prednost: za svaki piksel mozemo prirodno da pratimo sekundarne zrake -- refleksije, senke, refrakcije.
Ray tracing (image-order):
Za svaki piksel P na ekranu:
Kreiraj zrak R od kamere kroz piksel P
Pronadji najblizi trougao T koji R pogadja
Odredi boju P na osnovu T-ovog materijala, osvetljenja, refleksija...
Gde rasterizacija dominira
Rasterizacija je neuporedivo brza za primarnu vidljivost (primary visibility) -- odredjivanje koji trougao je vidljiv za svaki piksel. Razlozi:
-
Hardverska optimizacija: GPU-ovi su decenijama optimizovani za rasterizaciju. Fixed-function hardver za triangle setup, edge equations, interpolaciju, depth test -- sve je implementirano u silicijumu (Poglavlje 08, sekcija o fixed-function jedinicama).
-
Efikasna eliminacija: Frustum culling, backface culling, early-Z, Hi-Z -- sve ove optimizacije eliminisu ogroman broj trouglova pre nego sto se ikakav tezak posao obavi (Poglavlje 09, sekcije 9.6-9.8).
-
Sekvencijalna koherencija: Susedni pikseli obicno vide iste ili susedne trouglove, pa GPU moze da efikasno obradi grupe piksela zajedno (quad rendering, tile-based rendering).
-
Skalabilnost sa kompleksnoscu scene: Zahvaljujuci culling tehnikama, rasterizacija moze relativno efikasno da obradi scene sa milionima trouglova, jer vecina tih trouglova biva eliminisana pre rasterizacije.
Za tipicnu scenu sa 10 miliona trouglova, rasterizacija odredjuje primarnu vidljivost za ceo ekran od 1920x1080 za manje od jedne milisekunde. To je fantasticno brzo.
Gde ray tracing dominira
Ray tracing blista tamo gde rasterizacija pati -- kod sekundarnih efekata koji zahtevaju informacije o globalnoj sceni:
Refleksije
Sa rasterizacijom, refleksije su tezak problem. Screen-Space Reflections (SSR) rade samo sa informacijama koje su vec na ekranu -- ako objekat koji bi trebalo da se vidi u refleksiji nije na ekranu, SSR ga ne moze prikazati. Planar refleksije zahtevaju re-rendering cele scene iz ugla refleksije (dupli posao). Cubemap refleksije su staticki snimci okruzenja koji ne odrazavaju dinamicke promene.
Sa ray tracing-om? Ispucajte zrak refleksije od povrsine u pravcu refleksije, pronadjite sta pogadja, i to je boja refleksije. Radi za sve povrsine, sve uglove, sve objekte -- ukljucujuci i one koji nisu na ekranu. Nema limitacija screen-space pristupa.
Senke
Rasterizacione senke koriste shadow mape (Poglavlje 10) -- renderujte scenu iz ugla svetla, sacuvajte dubinu, pa uporedite sa dubinom iz kamere. Ovo generalno radi dobro, ali ima inherentne probleme: shadow acne, peter-panning, aliasing (pogotovo za velike svetlosne izvore ili svetla koja su daleko), ogranicena rezolucija, i problematicne meke senke.
Sa ray tracing-om? Za svaku tacku na povrsini, ispucajte zrak ka svetlu. Ako zrak pogodi nesto pre nego sto stigne do svetla -- tacka je u senci. Ako ne -- tacka je osvetljena. Za meke senke, ispucajte vise zraka ka razlicitim tackama na povrsini svetla (area light). Rezultat: fizicki tacne senke, bez aliasing artefakata, sa prirodno mekanim penumbrama.
Globalna iluminacija
Ovo smo detaljno obradili u Poglavlju 12. Rasterizacija nema prirodan nacin da izracuna indirektno osvetljenje -- mora da se oslanja na razlicite trikove (lightmape, light probe-ovi, SSGI). Ray tracing moze prirodno da prati puteve svetlosti kroz scenu, simulirajuci odbijanja i prenosenje boja izmedju povrsina.
Ambient Occlusion
Screen-space ambient occlusion (SSAO, HBAO, GTAO) radi samo sa informacijama iz depth buffer-a -- ne moze da "vidi" okluziju od objekata koji su van ekrana ili iza drugih objekata. Ray traced AO ispucava zrake u hemisferu iznad svake tacke i korektno odredjuje koliko je ta tacka okludirana, bez obzira na to sta je na ekranu.
Refrakcija i kaustike
Providni objekti sa refrakcijom (staklo, voda, dijamanti) su izuzetno tesko za renderovanje rasterizacijom. Ray tracing prirodno prati zrake svetlosti kroz providne medijume, lomeci ih prema Snell-ovom zakonu na svakoj granici. Kaustike (koncentrovane tacke svetlosti nastale fokusiranjem kroz providne objekte) su gotovo nemoguce za rasterizaciju, ali prirodan rezultat ray tracing-a.
Tabelarno poredjenje
| Aspekt | Rasterizacija | Ray Tracing |
|---|---|---|
| Primarna vidljivost | Izuzetno brza (< 1ms) | Sporija (zahteva BVH traversal) |
| Hardverska podrska | Decenijama optimizovana | Namenski hardver od 2018. |
| Refleksije | Screen-space (ogranicene) | Fizicki tacne, neogranicene |
| Senke | Shadow mape (aliasing, acne) | Fizicki tacne, meke penumbre |
| Globalna iluminacija | Trikovi i aproksimacije | Prirodan rezultat algoritma |
| Ambient Occlusion | Screen-space (ogranicen) | Tacna okluzija |
| Refrakcija | Izuzetno teska | Prirodna |
| Kompleksnost scene | Odlicno skalira | Zahteva BVH, zavisi od scene |
| Memorija | G-buffer, shadow mape | BVH + scene data |
| Paralelizacija | Odlicna na GPU | Dobra, ali manje koherentna |
Kljucni uvid
Ni rasterizacija ni ray tracing nisu "bolji" u apsolutnom smislu. To su razliciti alati za razlicite probleme. Moderna real-time grafika koristi oba pristupa -- rasterizaciju za ono u cemu je brza (primarna vidljivost, base pass), i ray tracing za ono u cemu je superioran (sekundarni efekti). Ovaj hibridni pristup je upravo ono sto UE5 koristi, i o tome cemo detaljno govoriti u sekciji 14.6.
14.2 Osnovni Algoritam Ray Tracing-a
Konceptualni model: Obrnuta svetlost
U fizickom svetu, svetlost putuje od izvora (sunce, sijalica) do objekta, odbija se, i eventualno pogadja nase oci. Ako bismo simulirali ovo doslovno -- pratili svaki foton od izvora svetlosti kroz scenu -- 99.99% fotona nikada ne bi stiglo do kamere. To bi bilo katastrofalno neefikasno.
Ray tracing okrce smer: umesto da pratimo svetlost od izvora ka kameri, pratimo zrake od kamere ka sceni. Ovo je fizicki ekvivalentno (princip reciprociteta svetlosti -- svetlost moze da putuje u oba smera duz istog puta), ali enormno efikasnije jer svaki zrak koji ispucamo garantovano doprinosi konacnoj slici.
Definicija zraka (ray)
Zrak u ray tracing-u je matematicki definisan kao poluprava (ray):
R(t) = O + t * D, t >= 0
Gde je:
- O (origin) -- pocetna tacka zraka (pozicija kamere za primarne zrake)
- D (direction) -- normalizovani vektor pravca
- t -- parametar koji odredjuje tacku duz zraka (t = 0 je origin, vece t je dalje od origin-a)
Za svaki piksel na ekranu, konstruisemo zrak koji polazi iz kamere i prolazi kroz taj piksel. Ovo je primarni zrak (primary ray ili camera ray).
Konstruisanje primarnih zraka
Da bismo generisali primarne zrake, moramo znati:
- Poziciju kamere (eye point)
- Pravac u kom kamera gleda (view direction)
- Polje pogleda (field of view -- FOV)
- Aspect ratio ekrana
Za perspektivnu projekciju, zamislite da ekran (image plane) lebdi ispred kamere na odredjenoj udaljenosti. Svaki piksel na ekranu odgovara tacki na tom image plane-u. Primarni zrak polazi iz kamere i prolazi kroz tu tacku:
Za piksel (i, j) na ekranu velicine (width, height):
// Normalizovane koordinate piksela [-1, 1]
ndc_x = (2 * (i + 0.5) / width - 1) * aspect_ratio * tan(fov / 2)
ndc_y = (1 - 2 * (j + 0.5) / height) * tan(fov / 2)
// Pravac zraka u world space-u
direction = normalize(ndc_x * right + ndc_y * up + forward)
// Primarni zrak
ray.origin = camera_position
ray.direction = direction
Obratite paznju na (i + 0.5) -- dodajemo 0.5 da bi zrak prolazio kroz centar piksela, a ne kroz ugao. Ovo je vazan detalj koji sprecava sistematski bias.
Nalazenje najblizeg preseka
Za svaki primarni zrak, moramo pronaci najblizi trougao u sceni koji taj zrak pogadja. Ovo je srce ray tracing-a i ujedno najskuplji korak -- o optimizaciji ovog koraka putem BVH-a govoricemo u sekciji 14.3.
Konceptualno, algoritam je jednostavan:
function trace_ray(ray, scene):
closest_hit = NULL
closest_t = INFINITY
for each triangle T in scene:
t = intersect(ray, T)
if t > 0 and t < closest_t:
closest_t = t
closest_hit = T
return closest_hit, closest_t
Kada pronadjemo najblizi trougao, znamo:
- Tacku preseka:
P = O + closest_t * D - Koji trougao je pogodjen (i samim tim koji materijal, koje UV koordinate, itd.)
- Baricentricne koordinate preseka unutar trougla (koje koristimo za interpolaciju normala, UV-ova, boja vertex-a -- sve kao sto GPU radi u rasterizaciji, Poglavlje 09, sekcija 9.4)
Izracunavanje boje
Jednom kada znamo tacku preseka i sve interpolirane atribute, mozemo da izracunamo boju tog piksela. U najjednostavnijem slucaju, ovo je isto sto i u rasterizaciji -- primenimo Phong ili PBR shading model (Poglavlje 10, 11) koristeci normalu, UV koordinate, i informacije o svetlima.
Ali ovde ray tracing pocinje da pokazuje svoju pravu snagu -- mozemo ispucati dodatne zrake iz tacke preseka.
Rekurzivni ray tracing (Whitted-style)
Turner Whitted je 1980. godine predlozio elegantno prosirenje osnovnog algoritma: na svakom mestu preseka, pored obicnog shading-a, ispucavamo dodatne zrake:
Shadow rays (zraci senki)
Za svaki izvor svetlosti u sceni, ispucamo zrak od tacke preseka ka svetlu:
shadow_ray.origin = hit_point + epsilon * normal // mali offset da izbegnemo self-intersection
shadow_ray.direction = normalize(light_position - hit_point)
Ako taj zrak pogodi bilo koji objekat pre nego sto stigne do svetla -- tacka je u senci tog svetla. Primetite koliko je ovo jednostavno i elegantno u poredjenju sa shadow mapama.
Vazan detalj: epsilon * normal offset je neophodan. Bez njega, zrak bi mogao da "pogodi" istu povrsinu iz koje polazi (self-intersection) zbog ogranicene preciznosti floating-point aritmetike. Ovo je analogno shadow acne problemu u shadow mapama, ali je resenje mnogo jednostavnije.
Reflection rays (zraci refleksije)
Ako je povrsina reflektivna (metal, ogledalo, mokra povrsina), ispucamo zrak u pravcu refleksije:
reflect_dir = D - 2 * dot(D, N) * N // zakon refleksije
reflection_ray.origin = hit_point + epsilon * normal
reflection_ray.direction = reflect_dir
Boja refleksije se dodaje boji piksela, pomnozena sa reflektivnoscu (Fresnel faktor, metalness, itd.).
Refraction rays (zraci refrakcije)
Ako je povrsina providna (staklo, voda), ispucamo zrak koji se lomi prema Snell-ovom zakonu:
// Snell-ov zakon: n1 * sin(theta1) = n2 * sin(theta2)
// n1 = indeks refrakcije medijuma iz kog dolazi zrak
// n2 = indeks refrakcije medijuma u koji ulazi zrak
eta = n1 / n2
cos_i = -dot(N, D)
sin2_t = eta * eta * (1 - cos_i * cos_i)
if sin2_t > 1.0:
// Totalna unutrasnja refleksija -- nema refrakcije
return reflection_ray
refract_dir = eta * D + (eta * cos_i - sqrt(1 - sin2_t)) * N
Rekurzija i zaustavljanje
Kljucna stvar: svaki od ovih sekundarnih zraka moze sam pogoditi reflektivnu ili providnu povrsinu, generisujuci nove zrake. Ovo vodi ka rekurzivnom stablu zraka:
function whitted_trace(ray, scene, depth):
if depth > MAX_DEPTH:
return background_color
hit = find_closest_intersection(ray, scene)
if hit == NULL:
return background_color
color = (0, 0, 0)
// Direktno osvetljenje sa shadow ray-evima
for each light L in scene:
shadow_ray = create_ray(hit.point, direction_to(L))
if not is_occluded(shadow_ray, scene):
color += compute_brdf(hit, L) * L.intensity
// Refleksija (ako je povrsina reflektivna)
if hit.material.reflectivity > 0:
reflect_ray = create_reflection_ray(ray, hit)
reflect_color = whitted_trace(reflect_ray, scene, depth + 1)
color += hit.material.reflectivity * reflect_color
// Refrakcija (ako je povrsina providna)
if hit.material.transparency > 0:
refract_ray = create_refraction_ray(ray, hit)
refract_color = whitted_trace(refract_ray, scene, depth + 1)
color += hit.material.transparency * refract_color
return color
MAX_DEPTH je obicno 4-8 za offline renderovanje. Za real-time, cak i 1-2 nivoa rekurzije mogu biti preskupi. UE5 tipicno koristi 1 bounce za RT refleksije.
Ogranicenja Whitted-style ray tracing-a
Vazno je razumeti sta Whitted-style ray tracing ne radi:
-
Nema difuzne interrefleksije. Whitted-style tracing samo prati zrake perfektne refleksije i refrakcije. Difuzno odbijanje (matte povrsine koje rasprsuju svetlost u sve pravce) je ignorisano -- upravo ono sto daje globalan osecaj osvetljenja (color bleeding, itd.). Za to je potreban path tracing (Poglavlje 12).
-
Nema mekih refleksija. Refleksije su savrseno ostre (mirror-like). Za glossy refleksije (blago zamucene, kao na poliranom metalu), potrebno je vise zraka po refleksiji (glossy reflection).
-
Nema area light efekata. Shadow rays idu ka tacki (point light), pa su senke uvek ostar hard shadow. Za meke senke od area light-ova, potrebno je vise shadow ray-eva.
Uprkos ovim ogranicenjima, Whitted-style ray tracing proizvodi drasticno bolje refleksije i refrakcije nego bilo koja rasterizaciona tehnika, i upravo ovi efekti su ono sto moderni real-time RT sistemi (ukljucujuci UE5) najcesce koriste.
14.3 BVH (Bounding Volume Hierarchy) -- Akceleraciona Struktura
Problem: Brute-force je neupotrebljiv
U prethodnom odeljku smo videli da za svaki zrak moramo pronaci najblizi presek sa trouglom u sceni. Naivni pristup -- testirati svaki zrak protiv svakog trougla -- ima slozenost O(N) po zraku, gde je N broj trouglova u sceni.
Koliko je to lose u praksi? Hajde da izracunamo:
- Scena sa 10 miliona trouglova (uobicajeno za modernu igru)
- Ekran rezolucije 1920x1080 = ~2 miliona piksela
- 1 primarni zrak po pikselu + 1 shadow ray po svetlu (pretpostavimo 1 svetlo) = ~4 miliona zraka
- Brute-force: 4 miliona zraka * 10 miliona trouglova = 40 triliona ray-triangle testova po frejmu
Cak i na GPU-u koji moze da obavi 10 milijardi testova u sekundi (sto je optimisticna pretpostavka), ovo bi trajalo 4.000 sekundi -- sat i po za jedan jedini frejm. Jasno, brute-force nije opcija.
Resenje: Prostorna podela
Ideja je jednostavna: ne testiraj zrak protiv svakog trougla, vec brzo eliminiši ogromne grupe trouglova koje zrak sigurno ne pogadja.
Zamislite da imate kutiju punu loptica razlicitih boja. Ako vas neko pita "kojim predmetima bi prolazio laserski zrak koji ide odavde do tamo?", vi ne biste testirali svaku lopticu pojedinacno. Prvo biste pogledali da li laserski zrak uopste prolazi kroz kutiju. Ako ne prolazi -- nijedna loptica nije pogodjena, gotovo. Ako prolazi -- podelite kutiju na dve manje kutije i ponovite proces.
Ovo je suština Bounding Volume Hierarchy (BVH) -- hijerarhijsko stablo ogranicavajucih kutija koje omogucava brzo odbacivanje velikih grupa trouglova.
Struktura BVH-a
BVH je binarno stablo gde:
- Svaki list (leaf node) sadrzi mali broj trouglova (tipicno 1-4)
- Svaki unutrasnji cvor (internal node) sadrzi Axis-Aligned Bounding Box (AABB) koji obuhvata sve trouglove u njegovom podstablu
- Koren (root) sadrzi AABB koji obuhvata celu scenu
AABB je najjednostavnija moguca ogranicavajuca kutija -- pravougaonik cije strane su paralelne sa osama koordinatnog sistema (X, Y, Z). Definisan je sa samo dve tacke: (min_x, min_y, min_z) i (max_x, max_y, max_z).
Vizuelizacija BVH stabla:
[Root AABB -- cela scena]
/ \
[AABB levi deo scene] [AABB desni deo scene]
/ \ / \
[AABB grupa A] [AABB grupa B] [AABB grupa C] [AABB grupa D]
/ \ / \ / \ / \
[T1,T2] [T3,T4] [T5,T6] [T7,T8] [T9,T10] [T11] [T12,T13] [T14,T15]
T = trougao (leaf node)
Traversal -- Kako zrak prolazi kroz BVH
Algoritam za nalazenje preseka zraka sa scenom koristeci BVH:
function bvh_intersect(ray, node):
// Test ray vs AABB ovog cvora
if not ray_intersects_aabb(ray, node.aabb):
return NO_HIT // Zrak ne pogadja ovu kutiju -- preskoci celo podstablo
if node.is_leaf:
// Testiraj zrak protiv svakog trougla u leaf-u
closest_hit = NO_HIT
for each triangle T in node.triangles:
hit = ray_triangle_intersect(ray, T)
if hit and hit.t < closest_hit.t:
closest_hit = hit
return closest_hit
// Rekurzivno testiraj oba deteta
hit_left = bvh_intersect(ray, node.left_child)
hit_right = bvh_intersect(ray, node.right_child)
// Vrati blizi pogodak
return closer(hit_left, hit_right)
Kljucna optimizacija: svaki test ray_intersects_aabb je izuzetno jeftin (6 operacija deljenja i poredjenja -- o ovome vise u sekciji o hardveru), a kada AABB test kaze "ne pogadja", eliminisemo sve trouglove u tom podstablu -- potencijalno milione trouglova jednim testom.
Slozenost traversal-a je O(log N) u prosecnom slucaju. Za scenu sa 10 miliona trouglova, to je oko 23 koraka umesto 10.000.000. Razlika izmedju O(N) i O(log N) je bukvalno razlika izmedju "sat i po po frejmu" i "real-time renderovanje".
Ray-AABB intersection test
Test da li zrak pogadja AABB (Axis-Aligned Bounding Box) je kritican jer se izvrsava za svaki cvor BVH-a tokom traversal-a. Algoritam koji se koristi je slab method (metod "plocastih" podela):
function ray_aabb_intersect(ray, aabb):
// Za svaku osu (X, Y, Z), izracunaj gde zrak ulazi i izlazi iz "ploca"
t_min_x = (aabb.min.x - ray.origin.x) / ray.direction.x
t_max_x = (aabb.max.x - ray.origin.x) / ray.direction.x
if t_min_x > t_max_x: swap(t_min_x, t_max_x)
t_min_y = (aabb.min.y - ray.origin.y) / ray.direction.y
t_max_y = (aabb.max.y - ray.origin.y) / ray.direction.y
if t_min_y > t_max_y: swap(t_min_y, t_max_y)
t_min_z = (aabb.min.z - ray.origin.z) / ray.direction.z
t_max_z = (aabb.max.z - ray.origin.z) / ray.direction.z
if t_min_z > t_max_z: swap(t_min_z, t_max_z)
// Zrak pogadja AABB samo ako se svi intervali preklapaju
t_enter = max(t_min_x, t_min_y, t_min_z)
t_exit = min(t_max_x, t_max_y, t_max_z)
return t_enter <= t_exit and t_exit >= 0
Ovo je 6 deljenja, 6 poredjenja, 2 max/min operacije. Izuzetno efikasno. I upravo ovo je operacija koja je implementirana u hardveru na NVIDIA RT Core-ovima (sekcija 14.5).
Konstrukcija BVH-a
Kako se BVH gradi? Postoji vise pristupa, ali najcesce korisceni je top-down konstrukcija:
function build_bvh(triangles):
node = new BVH_Node()
node.aabb = compute_aabb(triangles) // AABB svih trouglova
if triangles.count <= MAX_LEAF_SIZE:
node.triangles = triangles
node.is_leaf = true
return node
// Podeli trouglove na dve grupe
(left_triangles, right_triangles) = split(triangles)
node.left_child = build_bvh(left_triangles)
node.right_child = build_bvh(right_triangles)
return node
Kriticno pitanje: kako split() deli trouglove? Ovo direktno utice na kvalitet BVH-a i samim tim na performanse traversal-a.
SAH (Surface Area Heuristic) -- Optimalna podela
Najpoznatija i najkoriscenija heuristika za konstrukciju BVH-a je Surface Area Heuristic (SAH), koju je popularizovao MacDonald i Booth 1990. godine.
Ideja iza SAH-a je sledeca: verovatnoca da nasumicni zrak pogodi kutiju je proporcionalna povrsini te kutije. Ovo se izvodi iz geometrijske verovatnoce -- veci objekat je verovatnije da ce biti pogodjen od manjeg.
Kost (cost) jednog cvora BVH-a se definise kao:
Cost(node) = C_traversal + P(left) * Cost(left) + P(right) * Cost(right)
Gde je:
C_traversal-- cena testiranja jednog AABB-a (fiksna, mala)P(left)-- verovatnoca da zrak koji je pogodio roditeljsku kutiju pogodi i levu kutijuP(right)-- isto za desnu kutiju
Verovatnoca se aproksimira kao:
P(child) = Surface_Area(child.aabb) / Surface_Area(parent.aabb)
SAH trazi podelu koja minimizuje ukupnu cenu traversal-a. Intuitivno, zelimo da:
- Kutije dece budu sto manje (manja povrsina = manja verovatnoca pogotka)
- Kutije dece se sto manje preklapaju
- Trouglovi budu ravnomerno rasporedjeni
U praksi, SAH se racuna tako sto se za svaku osu (X, Y, Z) isprobaju razlicite pozicije podele (obicno na pozicijama centroida trouglova), izracuna cena za svaku podelu, i izabere podela sa najnizom cenom.
function sah_split(triangles, parent_aabb):
best_cost = INFINITY
best_split = NULL
for each axis in [X, Y, Z]:
// Sortiraj trouglove po centroidu duz ove ose
sorted = sort_by_centroid(triangles, axis)
// Isprobaj razlicite pozicije podele
for i from 1 to len(sorted) - 1:
left = sorted[0..i]
right = sorted[i..end]
left_aabb = compute_aabb(left)
right_aabb = compute_aabb(right)
// SAH cena
cost = C_TRAVERSAL +
len(left) * surface_area(left_aabb) / surface_area(parent_aabb) +
len(right) * surface_area(right_aabb) / surface_area(parent_aabb)
if cost < best_cost:
best_cost = cost
best_split = (axis, i)
return best_split
SAH proizvodii BVH-ove koji su tipicno 20-50% brzi za traversal od naivne podele (npr. podele na pola po medijani). Za scene sa neravnomernom distribucijom objekata (sto je tipicno za igre), razlika moze biti i veca.
TLAS i BLAS -- Dvonivovska hijerarhija
U modernim RT pipeline-ovima (DXR, Vulkan RT), BVH je organizovan u dva nivoa:
BLAS (Bottom-Level Acceleration Structure) -- BVH za jedan mesh. Sadrzi trouglove tog mesh-a organizovane u lokalnu BVH strukturu. BLAS se gradi jednom (ili retko, za dinamicne mesh-ove) i moze se deliti izmedju vise instanci istog mesh-a.
TLAS (Top-Level Acceleration Structure) -- BVH koji sadrzi instance BLAS-ova. Svaka instanca ima svoju transformacionu matricu (pozicija, rotacija, skaliranje). TLAS se gradi svaki frejm (jer se objekti pomeraju).
Vizuelizacija dvonivovske hijerarhije:
TLAS (Top-Level):
┌──────────────────────────────────────────────────────────────┐
│ Root AABB │
│ / \ │
│ [Grupa levo] [Grupa desno] │
│ / \ / \ │
│ [Inst A] [Inst B] [Inst C] [Inst D] │
│ Transform Transform Transform Transform │
│ -> BLAS 1 -> BLAS 1 -> BLAS 2 -> BLAS 3 │
└──────────────────────────────────────────────────────────────┘
BLAS 1 (npr. drvo): BLAS 2 (npr. kuca): BLAS 3 (npr. auto):
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ BVH stabla │ │ BVH kuce │ │ BVH auta │
│ sa svim │ │ sa svim │ │ sa svim │
│ trouglovima │ │ trouglovima │ │ trouglovima │
│ drveta │ │ kuce │ │ auta │
└─────────────────┘ └─────────────────┘ └─────────────────┘
Prednosti dvonivovskog sistema:
-
Instancing: Ako imate 1000 drveca u sceni, gradite BLAS za drvo samo jednom. TLAS sadrzi 1000 instanci koje dele isti BLAS, svaka sa svojom transformacijom. Usteda memorije: umesto 1000 kopija BVH-a, imate jednu.
-
Efikasno azuriranje: Kada se objekat pomeri, ne morate da ponovo gradite njegov BLAS -- samo azurirate transformacionu matricu u TLAS-u i ponovo izgradite TLAS (sto je mnogo jeftinije jer TLAS ima daleko manje cvorova od BLAS-ova).
-
Razdvojeni build time-ovi: BLAS se gradi jednom (ili retko), TLAS se gradi svaki frejm. Ovo raspodjeljuje racunski teret.
Traversal sa TLAS/BLAS
Kada zrak prolazi kroz dvonivovsku hijerarhiju, proces je:
1. Prolazi kroz TLAS da pronadje koje instance pogadja
2. Za svaku pogodenu instancu:
a. Transformisi zrak u lokalni prostor instance (primeni inverz transformacije)
b. Prolazi kroz BLAS te instance da pronadje koji trougao pogadja
c. Transformisi rezultat nazad u world space
3. Od svih pogodaka, vrati najblizi
Transformacija zraka u lokalni prostor instance je kriticna -- umesto da transformisemo sve trouglove u world space (skupo), transformisemo jedan zrak u lokalni prostor (jeftino).
BVH Refitting vs Rebuilding za dinamicne scene
Za staticne scene, BVH se gradi jednom i nikada se ne menja. Ali igre su dinamicne -- objekti se pomeraju, animiraju, pojavljuju i nestaju. Kako azurirati BVH?
Rebuild (potpuna ponovna izgradnja): Izgradite potpuno novi BVH od nule. Ovo garantuje optimalan kvalitet stabla, ali je skupo (moze trajati milisekunde za velike scene).
Refit (azuriranje postojeceg stabla): Struktura stabla ostaje ista, ali AABB-ovi se azuriraju da obuhvate nove pozicije trouglova. Ovo je mnogo brze (jer ne menja topologiju stabla), ali kvalitet stabla degradira -- kutije se "rasiravaju" i vise se preklapaju, sto znaci vise BVH cvorova koje zrak mora da testira.
Vizuelizacija refitting degradacije:
Pre pomeranja: Posle refitting-a:
┌─────────┐ ┌─────────┐ ┌───────────────────────────┐
│ Grupa A │ │ Grupa B │ │ Grupa A (prosirena) │
│ ●●● │ │ ●●● │ │ ●●● │
└─────────┘ └─────────┘ └───────────────────────────┘
┌─────────────────────┐
│ Grupa B (prosirena)│
│ ●●● │
└─────────────────────┘
Kutije se preklapaju => vise
traversal koraka za svaki zrak
U praksi, koristi se kombinacija:
- BLAS za animirane mesh-ove (npr. skinned characters) se refit-uje svaki frejm jer je brzo i degradacija je umerena
- TLAS se uvek rebuild-uje svaki frejm (jer je relativno mali -- sadrzi samo instance, ne trouglove)
- BLAS za staticne mesh-ove se gradi jednom i nikada ne menja
- Periodicni full rebuild za mesh-ove koji su se mnogo pomerili -- nekada svako N frejmova
UE5 automatski upravlja TLAS/BLAS azuriranjem -- vi kao korisnik kontrolisete samo koji aktori su ukljuceni u ray tracing scenu (flag Visible in Ray Tracing na komponentama).
14.4 Ray-Triangle Intersection -- Moller-Trumbore Algoritam
Zasto je ovo vazno
Ray-triangle intersection je operacija koja se izvrsava milijardama puta po frejmu. Svaki zrak koji dodje do leaf node-a BVH-a mora da testira presek sa jednim ili vise trouglova u tom leaf-u. Za tipican frejm sa par miliona zraka, svaki od kojih prolazi kroz 20-30 BVH cvorova i testira 2-4 trougla u leaf cvorovima, govorimo o desetinama miliona ray-triangle testova po frejmu.
Efikasnost ovog testa direktno utice na performanse celokupnog ray tracing pipeline-a. Razlika izmedju algoritma koji zahteva 30 operacija i onog koji zahteva 50 operacija je razlika od 40% u ukupnom vremenu ray tracing-a. Zato se koristi optimalan algoritam -- Moller-Trumbore.
Matematicka osnova
Imamo zrak definisan kao R(t) = O + t * D i trougao definisan sa tri temena V0, V1, V2.
Trazimo tacku koja pripada i zraku i trouglu. Svaka tacka na trouglu moze se izraziti pomocu baricentricnih koordinata (detaljno opisanih u Poglavlju 09, sekcija 9.4):
P = (1 - u - v) * V0 + u * V1 + v * V2
Gde su u, v >= 0 i u + v <= 1 (uslovi da je tacka unutar trougla).
Dakle, trazimo t, u, v takve da:
O + t * D = (1 - u - v) * V0 + u * V1 + v * V2
Preuredjivanjem:
O + t * D = V0 + u * (V1 - V0) + v * (V2 - V0)
Definisimo edge vektore:
E1 = V1 - V0
E2 = V2 - V0
T = O - V0 (vektor od temena trougla do pocetka zraka)
Sada imamo sistem linearnih jednacina:
[-D, E1, E2] * [t, u, v]^T = T
Ovo je sistem od 3 jednacine sa 3 nepoznate. Moze se resiti Cramerovim pravilom.
Moller-Trumbore implementacija
function moller_trumbore(ray_origin, ray_dir, v0, v1, v2):
E1 = v1 - v0
E2 = v2 - v0
// Pocni sa izracunavanjem determinante
P = cross(ray_dir, E2) // "P vektor"
det = dot(E1, P)
// Ako je determinanta blizu nule, zrak je paralelan sa trouglom
if abs(det) < EPSILON:
return NO_HIT
inv_det = 1.0 / det
// Izracunaj u parametar i testiraj granice
T = ray_origin - v0
u = dot(T, P) * inv_det
if u < 0.0 or u > 1.0:
return NO_HIT // Presek je van trougla
// Izracunaj v parametar i testiraj granice
Q = cross(T, E1) // "Q vektor"
v = dot(ray_dir, Q) * inv_det
if v < 0.0 or u + v > 1.0:
return NO_HIT // Presek je van trougla
// Izracunaj t -- udaljenost duz zraka
t = dot(E2, Q) * inv_det
if t < 0.0:
return NO_HIT // Presek je iza zraka
return (t, u, v) // Pogodak! Vrati udaljenost i baricentricne koordinate
Analiza efikasnosti
Prebrojimo operacije:
- 1 cross product (
P = cross(D, E2)) -- 6 mnozenja, 3 oduzimanja - 1 dot product (
det = dot(E1, P)) -- 3 mnozenja, 2 sabiranja - 1 deljenje (
inv_det = 1/det) - 1 dot product + 1 mnozenje za
u - 1 cross product (
Q = cross(T, E1)) - 1 dot product + 1 mnozenje za
v - 1 dot product + 1 mnozenje za
t - Ukupno: oko 27 mnozenja/oduzimanja i 1 deljenje
Ovo je izuzetno efikasno. Dodatna prednost: rani izlazi (early exit). Cim u ili v bude van granica, algoritam prekida -- ne racuna sve koeficijente ako vec zna da preseka nema. U praksi, vecina zraka promasi vecinu trouglova, pa se prosecni broj operacija po testu jos dodatno smanjuje.
Baricentricne koordinate kao izlaz
Primetite da Moller-Trumbore kao sporedni proizvod daje baricentricne koordinate (u, v). Ovo je izuzetno korisno jer se te koordinate koriste za interpolaciju atributa na mestu preseka:
// Interpolacija normala
interpolated_normal = (1 - u - v) * N0 + u * N1 + v * N2
// Interpolacija UV koordinata
interpolated_uv = (1 - u - v) * UV0 + u * UV1 + v * UV2
// Interpolacija bilo kog vertex atributa
interpolated_attr = (1 - u - v) * A0 + u * A1 + v * A2
Ovo je potpuno analogno interpolaciji u rasterizaciji (Poglavlje 09, sekcija 9.4), samo sto se u rasterizaciji baricentricne koordinate izracunavaju iz edge equations, a ovde iz Moller-Trumbore algoritma.
Watertight intersection
Jedan suptilan ali vazan problem: numericki nepreciznosti u floating-point aritmetici mogu dovesti do pukotina (cracks) izmedju susednih trouglova. Zrak moze "promasiti" tacno na ivici izmedju dva trougla, sto rezultira crnim pikselima ili "svetlecim" artefaktima.
Ovo se resava watertight (vodootpornim) ray-triangle intersection algoritmom koji garantuje da za svaku tacku na deljenim ivicama susednih trouglova, bar jedan od njih ce biti pogodjen. Moderna implementacija u hardveru (RT Core-ovi) koristi varijante ovog pristupa.
Backface culling u ray tracing-u
Opciono, ray-triangle test moze da ignorise trouglove cija je normala okrenuta od zraka (backface culling):
if det < EPSILON: // Umesto abs(det) < EPSILON
return NO_HIT // Ignorisi backface-ove
Ovo je korisno za zatvorene (solid) objekte jer znamo da zrak koji ulazi u objekat uvek pogadja front-face prvi. Za providne objekte, backface culling se obicno iskljucuje.
U DXR/Vulkan RT, backface culling se kontrolise per-ray flag-ovima (RAY_FLAG_CULL_BACK_FACING_TRIANGLES), sto daje fleksibilnost da se za isti BLAS koriste razliciti culling modovi za razlicite tipove zraka.
14.5 RT Hardver -- RT Core-ovi i Ray Accelerator-i
Zasto je specijalizovani hardver neophodan
Pre nego sto su postojali RT Core-ovi, ray tracing na GPU-u je radio u potpunosti u software-u -- BVH traversal i ray-triangle intersection su se izvrsavali na obicnim shader jezgrima (CUDA cores na NVIDIA, Compute Units na AMD).
Koliko je to sporo? Pogledajmo tipican slucaj:
- Jedan zrak prolazi kroz ~30 BVH cvorova (box testova) i testira ~4 trougla
- Svaki box test: ~20 FP operacija
- Svaki triangle test: ~27 FP operacija
- Ukupno po zraku: ~30 * 20 + 4 * 27 = ~708 FP operacija
Za 4 miliona zraka po frejmu (skroman broj): ~2.8 milijarde FP operacija samo za preseke. A to su samo preseci -- tu nisu ukljuceni shading, memory fetch, stack management za rekurziju...
U software-u na GPU-u, ovo je izuzetno neefikasno jer:
-
Divergentan pristup memoriji: Razliciti zraci pogadjaju razlicite delove BVH-a, sto znaci da razliciti thread-ovi u istom warp-u (Poglavlje 08) citaju potpuno razlicite memorijske lokacije. Ovo unistava cache koherenciju.
-
Kontrolna divergencija: Razliciti zraci prolaze kroz razlicit broj BVH cvorova, sto znaci da se thread-ovi u warp-u "rasinkronizuju" -- jedni zavrsavaju za 10 koraka, drugi za 50, a ceo warp mora da ceka najsporijeg (Poglavlje 08, sekcija o warp divergenciji).
-
Stack management: Rekurzivni BVH traversal zahteva stack (listu cvorova za posecivanje). Na GPU-u, stack se cuva u registrima ili shared memory, sto oduzima resurse od samog shading posla.
Rezultat: software ray tracing na GPU-u je 10-100x sporiji od dedikovanog hardvera za iste operacije. Na modenim GPU-ovima bez RT Core-ova, ray tracing je tehnicki moguc, ali previse spor za real-time koriscenje u vecini scenarija.
NVIDIA RT Core-ovi
NVIDIA je uvela RT Core-ove sa Turing arhitekturom (RTX 2000 serija, 2018). RT Core je specijalizovana hardverska jedinica koja obavlja dve operacije:
- Ray-AABB intersection test (box test za BVH traversal)
- Ray-triangle intersection test (Moller-Trumbore)
Ovo je fixed-function hardver (kao sto su rasterizeri, tessellatori, i ROP-ovi) -- ne moze se programirati, ali obavlja svoj posao enormno brze i energetski efikasnije od programabilnih shader jezgara.
Kako RT Core radi sa shader jezgrima
Rad RT Core-a je asinhron u odnosu na shader jezgra:
Tok izvrsavanja jednog zraka:
1. Shader jezgro generie zrak (ray generation shader)
2. Shader jezgro salje zrak RT Core-u
3. RT Core autonomno prolazi kroz BVH:
- Testira box za box, trougao za trouglom
- Shader jezgro je SLOBODNO da radi drugi posao
4. RT Core vraca rezultat (pogodak/promasaj) shader jezgru
5. Shader jezgro nastavlja sa shading-om
Ovo je kljucna prednost: dok RT Core radi traversal (sto moze trajati stotine ciklusa), shader jezgro moze da obraduje druge zrake ili radi shading za vec zavrsene zrake. Ovo je, u sustini, oblik latency hiding-a (Poglavlje 08) -- sakrivamo latenciju traversal-a korisnim radom na shader jezgrima.
Generacije NVIDIA RT hardvera
| Generacija | Arhitektura | RT Core verzija | Poboljsanja |
|---|---|---|---|
| RTX 2000 | Turing (2018) | RT Core 1.0 | Prva generacija. BVH traversal + ray-triangle intersection u hardveru. |
| RTX 3000 | Ampere (2020) | RT Core 2.0 | 2x throughput za ray-triangle intersection. Podrzava istovremeni traversal i intersection. |
| RTX 4000 | Ada Lovelace (2022) | RT Core 3.0 | 2x throughput nad Amperom. Opacity Micromap Engine (OMM) za efikasne alpha-tested senke. Displaced Micro-Mesh Engine (DMM). |
| RTX 5000 | Blackwell (2025) | RT Core 4.0 | Poboljsan throughput i efikasnost, Mega Geometry za masovne scene. |
Svaka generacija otprilike udvostucuje ray tracing performanse. RTX 4090 moze da obradi oko 191 Giga Ray/s (191 milijardi zraka u sekundi za najjednostavniji tip testa), sto je dramaticno vise od ~10 Giga Ray/s na RTX 2080 Ti.
Opacity Micromap (OMM) -- Ada Lovelace novitet
Jedno od znacajnijih poboljsanja u RT Core 3.0 je Opacity Micromap Engine. Problem koji resava: alpha-tested geometrija (lisca na drvecima, ograde, trava) zahteva pozivanje any-hit shader-a za svaki potencijalni presek, sto je skupo. OMM enkodira transparentnost direktno u mikro-mapu pridruzenu trouglu, omogucavajuci RT Core-u da presoci providne delove u hardveru, bez pozivanja shader-a. Za scene sa dosta vegetacije, ovo moze doneti 2-4x ubrzanje RT senki.
AMD Ray Accelerator-i
AMD je uveo hardversku podrsku za ray tracing sa RDNA 2 arhitekturom (RX 6000 serija, 2020):
RDNA 2 (2020): Svaki Compute Unit (CU) ima jedan Ray Accelerator koji obavlja ray-box intersection testove u hardveru. Medjutim, ray-triangle intersection se i dalje radi u software-u na shader jezgrima. Ovo je razlog zasto su RDNA 2 kartice bile sporije u ray tracing-u od konkurentnih NVIDIA kartica.
RDNA 3 (2022): Poboljsani Ray Accelerator-i sa vecim throughput-om za box testove i optimizovanim softverskim ray-triangle pathom. Jos uvek bez hardverskog ray-triangle testa.
RDNA 4 (2025): Znacajno poboljsani Ray Accelerator-i sa drasticno poboljsanim RT performansama. AMD je smanjio jaz sa NVIDIA-om u generaciji RDNA 4.
Intel Xe HPG Ray Tracing Units
Intel je sa Arc Alchemist (2022) GPU-ovima uveo Ray Tracing Units koji obavljaju i box i triangle intersection testove u hardveru, koncepcijski slicno NVIDIA-inom pristupu. Performanse su u rangu RDNA 2, ali su predstavljale solidan prvi korak.
Uporedjivanje pristupa
NVIDIA pristup: Box test u HW + Triangle test u HW = potpuna HW akceleracija
AMD RDNA 2/3: Box test u HW + Triangle test u SW = delimicna HW akceleracija
AMD RDNA 4: Znacajno poboljsana HW akceleracija
Intel Arc: Box test u HW + Triangle test u HW = potpuna HW akceleracija
NVIDIA-ina prednost u ray tracing-u velikim delom dolazi od toga sto su RT Core-ovi od prvog dana obavljali obe operacije u hardveru, dok je AMD u prva dva generacije radio triangle intersection u software-u. Ova razlika postaje sve manja sa svakom novom generacijom.
Prakticne implikacije za UE5
Kada u UE5 aktivirate hardware ray tracing (npr. za Lumen, RT Shadows, RT Reflections), performanse ce zavisiti od:
- Generacije RT hardvera -- noviji je bolji (ocigledno)
- Broja zraka po pikselu -- vise zraka = bolji kvalitet, ali i vece opterecenje RT Core-ova
- Kompleksnosti BVH-a -- vise trouglova u sceni = veci BVH = sporiji traversal
- Alpha-tested geometrije -- lisca, trava, ograde zahtevaju any-hit shader koji zaustavlja RT Core i vraca kontrolu shader jezgru (skupo, osim sa OMM na Ada Lovelace)
- Dinamicnih objekata -- zahtevaju BLAS refit/rebuild svaki frejm
Razumevanje ovih faktora pomaze vam da donosite informisane odluke o tome kada (ne) koristiti hardware RT u vasem projektu.
14.6 Hibridno Renderovanje -- Prakticni Pristup
Zasto ne mozemo "sve" ray trace-ovati
Potpun ray tracing (path tracing) za svaki piksel na ekranu sa dovoljnim brojem uzoraka za cistu sliku je i dalje previse skup za real-time. Kao sto smo videli u Poglavlju 12:
- 1920 x 1080 piksela * 64 SPP (samples per pixel -- minimum za pristojnu sliku) = ~132 miliona putanja
- Svaka putanja ima 3-5 bounce-ova
- Svaki bounce zahteva BVH traversal
- To je 400-660 miliona ray-scene testova po frejmu
- Na 60 FPS: 24-40 milijardi testova u sekundi
Moderni RTX 4090 moze da obradi oko 190 Giga Ray/s za jednostavne testove, ali realni throughput (sa shading-om, memory fetch-evima, any-hit shader-ima) je znatno nizi. Za potpun path tracing na punoj rezoluciji sa 64 SPP na 60 FPS, trebalo bi nam GPU 5-10x mocniji od RTX 4090.
Resenje: hibridno renderovanje. Koristite rasterizaciju za ono u cemu je brza (primarna vidljivost), a ray tracing selektivno za efekte gde je superioran.
Tipican hibridni pipeline
Evo kako moderan hibridni renderer radi (i ovo je, u sustini, ono sto UE5 radi sa hardware RT modom):
HIBRIDNI RENDERING PIPELINE:
1. BASE PASS (Rasterizacija)
├── Vertex/Pixel shader za sve objekte
├── Generisi G-buffer (albedo, normal, roughness, metalness, depth)
├── Rasterizuj primarnu vidljivost
└── Rezultat: G-buffer sa svim informacijama o svakom pikselu
2. RT SHADOWS (Ray Tracing -- opcionalno)
├── Za svaki piksel u G-bufferu, ispucaj shadow ray ka svetlu
├── 1 ray po pikselu po svetlu (ili vise za meke senke)
├── Rezultat: shadow mask (0 = senka, 1 = osvetljeno)
└── Alternativa: shadow mape (rasterizacija)
3. RT REFLECTIONS (Ray Tracing -- opcionalno)
├── Za piksele sa reflektivnim materijalima (roughness < threshold)
├── Ispucaj reflection ray u pravcu refleksije (+ jitter za glossy)
├── 1 ray po pikselu (sa denoising-om) ili 0.25-0.5 rays/pixel
├── Rezultat: reflection buffer
└── Alternativa: SSR + cubemape (rasterizacija)
4. RT GLOBAL ILLUMINATION (Ray Tracing -- opcionalno)
├── Ispucaj zrake u hemisferu za indirektno osvetljenje
├── Tipicno nizom rezolucijom (half-res ili quarter-res)
├── Rezultat: indirect lighting buffer
└── Alternativa: lightmape, probes, SSGI
5. RT AMBIENT OCCLUSION (Ray Tracing -- opcionalno)
├── Ispucaj kratke zrake u hemisferu za AO
├── Rezultat: AO buffer
└── Alternativa: SSAO/HBAO/GTAO
6. LIGHTING PASS (Compute)
├── Kombinuj G-buffer + shadows + reflections + GI + AO
├── Primeni PBR shading model
└── Rezultat: lit scene
7. POST-PROCESSING (Rasterizacija/Compute)
├── Bloom, tone mapping, DOF, motion blur, AA...
└── Rezultat: konacna slika
Primetite da su koraci 2-5 opcionalni -- svaki moze koristiti RT ili rasterizacionu alternativu, nezavisno od ostalih. Mozete imati RT senke ali rasterizacione refleksije, ili obrnuto. Ovo daje ogromnu fleksibilnost za balansiranje kvaliteta i performansi.
Koliko zraka po pikselu?
Evo tipicnih brojeva za hibridno renderovanje na 1080p:
| Efekat | Zraka po pikselu | Ukupno zraka (1080p) | Napomena |
|---|---|---|---|
| RT Shadows (1 svetlo) | 1 | ~2M | Hard shadow, no denoiser needed |
| RT Shadows (meke) | 4-8 | ~8-16M | Sa denoising-om moze i 1 SPP |
| RT Reflections | 0.5-1 | ~1-2M | Sa denoising-om, 0.5 SPP je cesto dovoljno |
| RT GI (half-res) | 0.25-1 | ~0.5-2M | Nizom rezolucijom + upscale |
| RT AO | 0.5-1 | ~1-2M | Kratki zraci, relativno jeftini |
| Ukupno | ~5-22M | Zavisi od konfiguracije |
Uporedite ovo sa path tracing-om koji bi zahtevao 64-1024 SPP za cistu sliku (132M-2.1B zraka). Hibridni pristup smanjuje broj zraka za 10-100x, cesto koristeci manje od 1 zrak po pikselu za pojedinacne efekte.
Denoising -- Kljuc za nizak broj uzoraka
Sa samo 0.5-1 zraka po pikselu, rezultat je izuzetno sumovit (noisy). Kako onda dobijamo cistu sliku? Odgovor: denoising -- algoritmi koji rekonstruisu cistu sliku iz sumovitog ulaza.
Temporal denoising (vremensko filtriranje)
Osnovna ideja: iskoristite informacije iz prethodnih frejmova. Ako se kamera sporzo pomera, mnogi pikseli "vide" istu stvar u uzastopnim frejmovima. Akumuliranjem uzoraka tokom vremena, efektivno povecavate SPP:
// Temporal accumulation (uprisceno)
current_sample = ray_trace(pixel)
history_sample = reproject(pixel, previous_frame) // motion vectors
if is_valid(history_sample):
output = lerp(current_sample, history_sample, 0.9) // 90% history, 10% new
else:
output = current_sample // Nema validne istorije (disocclusion)
Problem: temporal denoising moze dovesti do ghosting-a (duchova) -- artefakata gde stari podaci "zaostaju" na mestu gde se scena promenila (objekat se pomerio, kamera se okrenula). Moderni denoiser-i koriste razlicite heuristike za detekciju i odbacivanje nevazecih podataka iz istorije.
Prostorni (spatial) denoising
Kombinuje informacije iz susednih piksela. Ali ne slepo -- koristi informacije iz G-buffer-a (normale, dubina, albedo) da bi identifikovao koje piksele je "bezbedno" pomesati:
// Edge-aware spatial filter (A-Trous wavelet filter, uprisceno)
for each neighbor pixel N around current pixel P:
weight = gaussian_weight(distance(P, N))
weight *= normal_weight(normal(P), normal(N)) // Manja tezina za razlicite normale
weight *= depth_weight(depth(P), depth(N)) // Manja tezina za razlicite dubine
weight *= albedo_weight(albedo(P), albedo(N)) // Manja tezina za razlicite boje
filtered += sample(N) * weight
total_weight += weight
output = filtered / total_weight
Ovo sacuvava ostre ivice (jer susedni pikseli na razlicitim objektima imaju razlicite normale/dubine i dobijaju malu tezinu), dok gladjaca sum na povrsinama.
AI denoising
NVIDIA-in NRD (NVIDIA Real-Time Denoiser) i slicni sistemi koriste neuronske mreze trenirane na ogromnim datasetovima parova (sumovita slika -> cista referentna slika). Ovi denoiser-i su neverovatno efikasni i mogu da rekonstruisu ubedljive slike iz svega 1 SPP.
UE5 koristi sopstveni denoising pipeline koji kombinuje temporalno i prostorno filtriranje, sa specificnim podesavanjima za svaki efekat (senke, refleksije, GI, AO).
Artefakti denoising-a
Denoising nije savrsen. Cesti artefakti:
- Ghosting: Zaostali podaci iz prethodnih frejmova vidljivi kao "duchovi" oko pokretnih objekata.
- Blurriness: Preterano filtriranje gubitak detalja, pogotovo na niskim SPP vrednostima.
- Temporal lag: Promene u osvetljenju (npr. eksplozija) se pojavljuju sa zakasnjenjem jer denoiser tezi da zadrzi stare podatke.
- Boiling/shimmering: Na mestima gde denoiser nema dovoljno podataka, pikseli "trepcanu" izmedju frejmova.
Balansiranje izmedju ovih artefakata i kvaliteta slike je jedna od najvecih tehnickih izazova u real-time ray tracing-u. Vecina korisnika UE5 ce ove artefakte kontrolisati indirektno -- kroz izbor SPP, kvaliteta denoiser-a, i rezolucije RT efekata.
14.7 DXR i Vulkan RT Pipeline
Novi tip pipeline-a
Do sada smo govorili o algoritmima i hardveru. Sada hajde da pogledamo softverski interfejs -- kako vi (ili engine poput UE5) zapravo komunicirate sa GPU-om da biste pokrenuli ray tracing.
Tradicionalni rasterizacioni pipeline (Poglavlje 07) je linearan: vertex shader -> rasterizer -> pixel shader. Svaka faza ima jasno definisan ulaz i izlaz, i tok podataka je uvek u istom smeru.
Ray tracing pipeline je fundamentalno drugaciji -- nema linearnog toka. Zrak moze da pogodi bilo sta u sceni, sto moze izazvati bilo koji shader. Tok izvrsavanja zavisi od sadrzaja scene i od toga sta svaki zrak pogadja.
DXR (DirectX Raytracing) je Microsoft-ov API za ray tracing, uveden u DirectX 12 (2018). Vulkan RT je Khronos-ov ekvivalent za Vulkan. Oba API-ja definisu istu konceptualnu strukturu sa pet tipova shader-a.
Pet tipova RT shader-a
1. Ray Generation Shader
Uloga: "Polazna tacka" ray tracing-a. Ovaj shader se pokrece za svaki piksel (ili za svaki element radne grupe) i odgovoran je za kreiranje i "ispucavanje" zraka.
[shader("raygeneration")]
void RayGenShader()
{
// Odredi koji piksel obradujemo
uint2 pixel = DispatchRaysIndex().xy;
uint2 dimensions = DispatchRaysDimensions().xy;
// Konstruisi primarni zrak za ovaj piksel
RayDesc ray;
ray.Origin = cameraPosition;
ray.Direction = ComputeRayDirection(pixel, dimensions);
ray.TMin = 0.001; // Minimalna udaljenost (izbegava self-intersection)
ray.TMax = 10000.0; // Maksimalna udaljenost
// Payload -- struktura u kojoj ce se vratiti rezultat
HitPayload payload;
payload.color = float3(0, 0, 0);
// Ispucaj zrak!
TraceRay(
accelerationStructure, // TLAS
RAY_FLAG_NONE, // Ray flags
0xFF, // Instance mask
0, // Hit group index
0, // Multiplier for geometry index
0, // Miss shader index
ray, // Ray descriptor
payload // Payload
);
// Zapisi rezultat
outputTexture[pixel] = float4(payload.color, 1.0);
}
Analogija: ray generation shader je kao "main()" funkcija ray tracing-a. Sve pocinje i zavrsava se ovde.
2. Intersection Shader
Uloga: Definise kako se testira presek zraka sa geometrijom. Za trouglove, ovaj shader nije potreban jer RT Core obavlja Moller-Trumbore test u hardveru. Ali za custom geometriju (sfere, kutije, implicite povrsine, volumetrijske primitive), morate napisati svoj intersection shader.
[shader("intersection")]
void SphereIntersectionShader()
{
// Custom intersection test za sferu
float3 center = ObjectRayOrigin(); // U lokalnom prostoru objekta
float radius = 1.0;
float3 oc = WorldRayOrigin() - center;
float a = dot(WorldRayDirection(), WorldRayDirection());
float b = 2.0 * dot(oc, WorldRayDirection());
float c = dot(oc, oc) - radius * radius;
float discriminant = b * b - 4 * a * c;
if (discriminant >= 0)
{
float t = (-b - sqrt(discriminant)) / (2.0 * a);
if (t >= RayTMin() && t <= RayTCurrent())
{
// Prijavi pogodak
SphereAttributes attr;
attr.normal = normalize(oc + t * WorldRayDirection());
ReportHit(t, 0, attr);
}
}
}
U praksi, vecina igara koristi iskljucivo trouglove i ne treba im custom intersection shader. UE5 koristi ugradjeni triangle intersection.
3. Any-Hit Shader
Uloga: Poziva se za svaki potencijalni presek tokom traversal-a, pre nego sto se utvrdi da li je taj presek zaista najblizi. Najcesce koriscen za alpha testing -- odbacivanje preseka gde je tekstura providna.
[shader("anyhit")]
void AlphaTestAnyHitShader(inout HitPayload payload, BuiltInTriangleIntersectionAttributes attr)
{
// Izracunaj UV na mestu preseka
float2 uv = InterpolateUV(attr.barycentrics);
// Sampleuj alpha iz teksture
float alpha = alphaTexture.SampleLevel(sampler, uv, 0).a;
// Ako je alpha ispod praga, ignorisi ovaj presek
if (alpha < 0.5)
{
IgnoreHit(); // Kazi RT Core-u da nastavi trazenje
}
// Inace, AcceptHitAndEndSearch() ili ostavi RT Core da nastavi
}
Performansni uticaj: Any-hit shader je skup jer prekida rad RT Core-a, vraca kontrolu shader jezgru, izvrsava shader, i potencijalno vraca kontrolu nazad RT Core-u. Za scene sa dosta alpha-tested geometrije (vegetacija, ograde), ovo moze biti znacajan bottleneck. Zato je Opacity Micromap (OMM) na Ada Lovelace toliko vazan -- eliminise potrebu za any-hit shader-om u mnogim slucajevima.
4. Closest-Hit Shader
Uloga: Poziva se jednom za najblizi presek zraka sa scenom. Ovo je ekvivalent pixel shader-a u rasterizaciji -- ovde se radi shading.
[shader("closesthit")]
void PrimaryClosestHitShader(inout HitPayload payload,
BuiltInTriangleIntersectionAttributes attr)
{
// Interpoliraj atribute na mestu preseka
float3 normal = InterpolateNormal(attr.barycentrics);
float2 uv = InterpolateUV(attr.barycentrics);
float3 worldPos = WorldRayOrigin() + RayTCurrent() * WorldRayDirection();
// Sampleuj materijal
float3 albedo = albedoTexture.SampleLevel(sampler, uv, 0).rgb;
// Shadow ray ka svetlu
RayDesc shadowRay;
shadowRay.Origin = worldPos + normal * 0.001; // Offset
shadowRay.Direction = normalize(lightPosition - worldPos);
shadowRay.TMin = 0.001;
shadowRay.TMax = length(lightPosition - worldPos);
ShadowPayload shadowPayload;
shadowPayload.isInShadow = true;
TraceRay(accelerationStructure, RAY_FLAG_ACCEPT_FIRST_HIT_AND_END_SEARCH,
0xFF, 1, 0, 1, shadowRay, shadowPayload);
// Izracunaj osvetljenje
float NdotL = max(dot(normal, shadowRay.Direction), 0);
float shadow = shadowPayload.isInShadow ? 0.0 : 1.0;
payload.color = albedo * NdotL * shadow * lightColor;
}
Obratite paznju: iz closest-hit shader-a mozete ispucati nove zrake (kao sto vidimo sa shadow ray-om iznad). Ovo je rekurzija o kojoj smo govorili u sekciji 14.2.
5. Miss Shader
Uloga: Poziva se kada zrak ne pogodi nista u sceni. Obicno vraca boju neba ili okruzenja.
[shader("miss")]
void PrimaryMissShader(inout HitPayload payload)
{
// Zrak nije pogodio nista -- vrati boju neba
float3 direction = WorldRayDirection();
float t = 0.5 * (direction.y + 1.0); // Gradijent od horizonta
payload.color = lerp(float3(1, 1, 1), float3(0.5, 0.7, 1.0), t);
}
[shader("miss")]
void ShadowMissShader(inout ShadowPayload payload)
{
// Shadow ray nije pogodio nista -- tacka NIJE u senci
payload.isInShadow = false;
}
Shader Binding Table (SBT)
Jedan od najkompleksnijih delova DXR/Vulkan RT pipeline-a je Shader Binding Table (SBT). Problem koji resava: u sceni imate razlicite objekte sa razlicitim materijalima. Kada zrak pogodi objekat, GPU mora da zna koji closest-hit shader da pozove (i sa kojim parametrima).
SBT je tabela u memoriji koja mapira kombinaciju (geometrija, tip zraka) na odgovarajuci shader i njegove resurse:
Shader Binding Table:
Index | Shader | Resursi
------|-----------------|----------------------------
0 | RayGen shader | output texture, camera data
1 | Miss shader 0 | sky texture
2 | Miss shader 1 | (shadow miss -- nema resursa)
3 | HitGroup 0 | albedo_tex_A, normal_tex_A (materijal A)
4 | HitGroup 1 | albedo_tex_B, normal_tex_B (materijal B)
5 | HitGroup 2 | albedo_tex_C, normal_tex_C (materijal C)
...
Hit Group je kombinacija closest-hit + any-hit + intersection shader-a za jedan materijal.
Formula za odredjivanje koji hit group se koristi:
SBT_index = ray_contribution_to_hit_group_index +
(multiplier_for_geometry_contribution * geometry_index) +
instance_contribution_to_hit_group_index
UE5 automatski gradi i upravlja SBT-om -- vi kao korisnik nikada ne radite sa njim direktno. Ali razumevanje SBT-a pomaze da razumete zasto odredjene operacije (npr. mnogo razlicitih materijala u RT sceni) mogu biti skupe.
Razlike izmedju DXR i Vulkan RT
Konceptualno, DXR i Vulkan RT su gotovo identicki -- oba API-ja su razvijana u saradnji sa istim hardverskim proizvodjacima. Razlike su uglavnom u terminologiji i API sintaksi:
| Koncept | DXR (DirectX 12) | Vulkan RT |
|---|---|---|
| Acceleration Structure | D3D12_RAYTRACING_ACCELERATION_STRUCTURE | VkAccelerationStructureKHR |
| Tracing funkcija | TraceRay() (HLSL) | traceRayEXT() (GLSL) |
| Shader jezik | HLSL | GLSL (sa RT ekstenzijama) |
| Pipeline objekat | D3D12_STATE_OBJECT | VkRayTracingPipelineKHR |
| Shader Binding Table | Manual layout | VkStridedDeviceAddressRegionKHR |
UE5 koristi DXR na Windows-u (DirectX 12 backend) i Vulkan RT na platformama gde je dostupan. Oba backend-a su funkcionalno ekvivalentni za krajnjeg korisnika.
Razlika izmedju rasterizacionog i RT pipeline-a
Hajde da uporedimo dva pipeline-a:
RASTERIZACIONI PIPELINE (linearan):
Input Assembler -> Vertex Shader -> [Tessellation] -> [Geometry Shader]
-> Rasterizer -> Pixel Shader -> Output Merger
- Tok je uvek isti, od leva ka desno
- Svaka faza ima jasno definisan ulaz/izlaz
- Nema grananja na nivou pipeline-a
RAY TRACING PIPELINE (nelinearan, event-driven):
Ray Generation Shader
|
+---> TraceRay()
|
+---> BVH Traversal (RT Core)
|
+---> Intersection Shader (za custom geometriju)
+---> Any-Hit Shader (za alpha testing)
|
+---> Closest-Hit Shader (pogodak)
| |
| +---> TraceRay() (rekurzija!)
|
+---> Miss Shader (promasaj)
|
+---> TraceRay() (rekurzija!)
Kljucna razlika: RT pipeline je data-driven -- koji shader ce se izvrsiti zavisi od toga sta zrak pogadja, sto zavisi od sadrzaja scene. Ovo je fundamentalno razlicito od rasterizacije gde je tok unapred poznat.
Ova razlika ima implikacije za performanse: u rasterizaciji, GPU moze unapred da zna koji shader ce se izvrsiti i da pripremi resurse. U ray tracing-u, GPU ne zna koji closest-hit shader ce biti potreban dok zrak ne zavrsi traversal -- sto moze dovesti do cache promaisaja i pipeline stall-ova.
14.8 Ray Tracing u Unreal Engine 5
Pregled RT mogucnosti u UE5
UE5 nudi vise ray tracing funkcionalnosti, svaku sa svojim podesavanjima, performansnim trosovima, i vizuelnim poboljsanjima. Evo pregleda:
RT Refleksije (Ray Traced Reflections)
Sta rade: Za reflektivne povrsine (metal, staklo, mokre povrsine), ispucavaju reflection ray u pravcu refleksije i vracaju boju onoga sto zrak pogodi. Zamenjuju SSR (Screen-Space Reflections) i cubemap fallback.
Prednosti nad rasterizacijom:
- Refleksije van ekrana (off-screen reflections) -- vide objekte koji nisu na ekranu
- Tacne refleksije za zakrivljene povrsine
- Nema "hall of mirrors" artefakata SSR-a
- Multi-bounce refleksije (ogledalo u ogledalu)
Performansni trosak: Umeren do visok. Zavisi od broja reflektivnih piksela, broja zraka, i kompleksnosti scene.
Podesavanja u UE5:
r.RayTracing.Reflections-- ukljuci/iskljucir.RayTracing.Reflections.MaxRoughness-- prag hrapavosti iznad kog se RT refleksije ne koriste (veci roughness = difuznija refleksija = manje vidljiva razlika, pa se moze preskociti za ustedu)r.RayTracing.Reflections.MaxBounces-- broj bounce-ova za refleksije (obicno 1)r.RayTracing.Reflections.SamplesPerPixel-- broj uzoraka po pikselu
RT Senke (Ray Traced Shadows)
Sta rade: Za svako svetlo, ispucavaju shadow ray od svake vidljive tacke ka svetlu. Zamenjuju shadow mape.
Prednosti nad rasterizacijom:
- Nema shadow acne/peter-panning artefakata
- Nema aliasing-a (ravnomerno precizne na svim udaljenostima)
- Fizicki tacne meke senke od area light-ova (penumbra)
- Nema kaskadnih artefakata (CSM transition banding)
Performansni trosak: Umeren. Shadow ray-evi su relativno jeftini jer su obicno kratki i koriste RAY_FLAG_ACCEPT_FIRST_HIT_AND_END_SEARCH (ne moraju da nadju najblizi pogodak, samo da utvrde da li postoji ikakav pogodak).
Podesavanja u UE5:
r.RayTracing.Shadows-- ukljuci/iskljuci- Per-light:
Cast Ray Traced Shadowsna svetlosnoj komponenti r.RayTracing.Shadows.SamplesPerPixel-- uzorci po pikselu (vise = mekse senke ali skuplje)
RT Globalna Iluminacija (Ray Traced GI)
Sta rade: Ispucavaju zrake u hemisferu iznad svake vidljive tacke za izracunavanje indirektnog osvetljenja. Ovo je najskuplja RT funkcionalnost.
Prednosti:
- Dinamicna GI bez bake-ovanja (lightmapa)
- Tacno color bleeding
- Reaguje na promene u osvetljenju u real-time-u
Performansni trosak: Visok. GI zahteva vise zraka po pikselu i vise bounce-ova nego senke ili refleksije.
Napomena: U UE5, Lumen je preporuceni sistem za GI (Poglavlje 25). Lumen moze da koristi hardware RT kao backend za tracanje zraka, ali ima i software tracing opciju. Standalone RT GI (bez Lumen-a) je takodje dostupno, ali Lumen nudi bolji kvalitet uz isti ili nizi performansni trosak.
RT Ambient Occlusion (Ray Traced AO)
Sta rade: Ispucavaju kratke zrake u hemisferu za odredjivanje koliko je svaka tacka "zatvorena" okolnom geometrijom.
Prednosti nad SSAO/HBAO:
- Nema screen-space ogranicenja (vidi okluziju od objekata van ekrana)
- Tacan AO za kompleksnu geometriju
- Nema halo artefakata
Performansni trosak: Nizak do umeren (kratki zraci = brzi BVH traversal).
Lumen Hardware RT Mode
Lumen (Poglavlje 25) moze da radi u dva moda:
Software Tracing: Koristi signed distance fields (SDF) i mesh distance fields za tracanje zraka. Brze, ali manje precizno -- geometrija je aproksimirana distance field-ovima.
Hardware RT: Koristi pravi BVH i RT Core-ove za tracanje. Preciznije (vidi stvarne trouglove), ali skuplje.
Kada koristiti hardware RT mod:
- Scene sa puno sitnih detalja koji se gube u SDF-u
- Potreba za preciznim refleksijama malih objekata
- Dostupan je dovoljno mocan GPU (RTX 3060 ili bolji)
Kada koristiti software tracing:
- Potreban je nizi performansni trosak
- Scena nema mnogo sitnih detalja
- Cilj je konzolne platforme ili slabiji PC hardware
Podesavanje: Project Settings > Engine > Rendering > Lumen > Use Hardware Ray Tracing when available
Path Tracer u UE5
UE5 ima i potpuni Path Tracer -- offline referentni renderer koji koristi potpun path tracing sa neogranicenim bounce-ovima, kausticima, i punom fizickom simulacijom svetlosti. Ovo nije za real-time -- koristi se za:
- Generisanje referentnih slika za validaciju Lumen podesavanja
- Offline renderovanje visokog kvaliteta (filmski snippet-i)
- Archviz vizualizacije
- Validacija materijala
Aktivira se kroz Viewport > View Mode > Path Tracing ili konzolnom komandom.
Prakticni vodic: Kada ukljuciti hardware RT
| Situacija | Preporuka |
|---|---|
| PC sa RTX 3060+ | Razmotriti hardware RT za refleksije i senke |
| PC sa RTX 4070+ | Hardware RT za Lumen + RT refleksije je izvodljiv |
| PC sa starijim GPU bez RT | Software tracing (Lumen), bez hardware RT |
| Konzole (PS5/Xbox Series X) | Ogranicen hardware RT (pazi na budget) |
| Mobile | Bez ray tracing-a |
| Archviz/cinematika | Maksimalan RT kvalitet, Path Tracer za finalne slike |
| Kompetitivna igra (60+ FPS) | Minimalan RT ili bez RT-a |
Kontrola RT vidljivosti
Ne mora svaki objekat u sceni da bude vidljiv za ray tracing. UE5 omogucava fino-granularnu kontrolu:
- Per-actor:
Details > Rendering > Visible in Ray Tracing-- iskljucite RT za objekte koji ne doprinose vizuelnom kvalitetu (npr. mali dekorativni elementi) - Per-component: Isto podesavanje na nivou komponente
- Geometry mask: Instance inclusion/exclusion maskovi u TLAS-u
Smanjivanje broja objekata u RT sceni direktno smanjuje velicinu BVH-a (brzi traversal), memorijsku potrosnju (manji BLAS-ovi), i trosak TLAS rebuild-a.
RT i Nanite
Vazan detalj: u trenutnom stanju UE5, Nanite mesh-ovi i hardware ray tracing imaju ogranicenu kompatibilnost. Nanite koristi svoj softverski rasterizer (Poglavlje 08) koji ne generise trouglove na tradicionalan nacin, sto otezava izgradnju BLAS-ova.
UE5 resava ovo generisanjem proxy mesh-ova za Nanite objekte koji se koriste u RT sceni. Ovi proxy mesh-ovi su nize rezolucije od punog Nanite mesh-a, sto znaci da RT refleksije/senke mogu prikazati manje detalja nego rasterizovana slika. Epic kontinuirano poboljsava ovu integraciju sa svakim UE5 update-om.
Debugging RT u UE5
UE5 nudi vise alata za debagovanje RT problema:
-
Ray Tracing Debug view modes:
Viewport > Show > Ray Tracing Debug-- prikaz BVH-a, performansi po pikselu, broja traverzala, itd. -
Konzolne komande:
r.RayTracing.ForceAllRayTracingEffects 0/1-- globalno ukljuci/iskljuci sve RT efektestat gpu-- prikazuje vreme provedeno u svakoj fazi renderovanja, ukljucujuci RTProfileGPU-- detaljni profil GPU timeline-a
-
RenderDoc / NVIDIA Nsight: Eksterni alati za duboku analizu RT pipeline-a, BVH kvaliteta, shader performansi
14.9 Napredni Koncepti i Buducnost
Ray Tracing i koherencija
Jedan od najvecih izazova ray tracing-a na GPU-u je koherencija zraka. U rasterizaciji, susedni pikseli obicno obraduju iste ili susedne trouglove, sto znaci svestandnu koherenciju u pristupu memoriji i izvrsavanju shader-a.
U ray tracing-u, sekundarni zraci (refleksije, GI) mogu ici u potpuno razlicitim pravcima, pogadjati potpuno razlicite objekte, i zahtevati potpuno razlicite materijale. Ovo znaci:
- Divergencija u warp-u: Thread-ovi u istom warp-u obraduju zrake koje prolaze kroz razlicite delove BVH-a -- razlicit broj koraka, razliciti leaf cvorovi
- Cache promaisaji: Razliciti zraci citaju razlicite delove BVH-a i razlicite teksture
- Shader divergencija: Razliciti zraci pogadjaju razlicite materijale, zahtevajuci razlicite closest-hit shader-e
Tehnike za poboljsanje koherencije:
-
Ray sorting/reordering: Grupisanje zraka po smeru ili lokaciji pre ispucavanja, tako da zraci u istom warp-u idu u slicnim pravcima. NVIDIA Shader Execution Reordering (SER), uveden sa Ada Lovelace, radi upravo ovo u hardveru.
-
Wavefront path tracing: Umesto da svaki thread prati jedan zrak od pocetka do kraja (sto dovodi do divergencije), razdvojite pipeline na faze i grupisite zrake po fazi -- svi zraci koji treba da urade BVH traversal se obraduju zajedno, svi koji treba da urade shading se obraduju zajedno.
-
Ray compaction: Eliminisanje "mrtvih" zraka (onih koji su promasili ili su vec kompletni) iz radne grupe, tako da GPU ne trosi resurse na neaktivne thread-ove.
Bindless resources i ray tracing
U rasterizaciji, svaki draw call ima jasno definisane resurse (teksture, buffer-i). U ray tracing-u, zrak moze da pogodi bilo koji objekat u sceni, sto znaci da bilo koji materijal moze biti potreban u bilo kom trenutku.
Ovo zahteva bindless resources (ili "descriptor indexing") -- sposobnost pristupa proizvoljnom broju tekstura i buffer-a bez eksplicitnog bindovanja po draw call-u. Svi resursi scene su dostupni kroz globalnu tabelu deskriptora, a closest-hit shader koristi indeks iz BVH instance podataka da pristupi tacno onim teksturama koje treba.
UE5 koristi bindless pristup za RT shading, sto je jedan od razloga zasto zahteva DirectX 12 ili Vulkan (DirectX 11 ne podrzava bindless resources u dovoljnoj meri).
Full path tracing u real-time -- koliko smo blizu?
Na pitanje "kada cemo moci da radimo potpun path tracing na 60 FPS na 4K?" -- odgovor zavisi od definicije "potpunog":
Sa agresivnim denoising-om (1-4 SPP): Vec je moguce na najjacim GPU-ovima za jednostavnije scene. NVIDIA je demonstrirala Portal RTX i Cyberpunk 2077 sa "punim" path tracing-om, ali uz 1-2 SPP i agresivni DLSS denoising. Vizuelno ubedljivo, ali sa primetnim artefaktima denoising-a.
Sa cisto cenom slikom (64+ SPP): Jos uvek daleko. Potrebno je 30-50x poboljsanje u RT performansama ili fundamentalno novi pristup (npr. napredni AI reconstruction).
Predikcija: Svaka generacija GPU-a donosi otprilike 2x poboljsanje RT performansi. Sa 5-6 generacija (10-15 godina), potpun path tracing bi mogao postati praktican za igre. Medjutim, rezolucije i kompleksnost scena takodje rastu, pa je moguce da ce hibridni pristup ostati dominantan jos dugo.
Mesh shader-i i ray tracing
Sa uvdoenjem mesh shader-a (NVIDIA Turing+, AMD RDNA 3), javlja se pitanje integracije sa ray tracing-om. Mesh shader-i omogucavaju proceduralno generisanje geometrije na GPU-u -- ali ta geometrija mora biti ukljucena u BVH da bi bila vidljiva za zrake.
UE5 Nanite vec koristi vrstu softverskog mesh shader pipeline-a. Integracija Nanite mesh-ova sa RT BVH-om je aktivan pravac razvoja i jedno od najvaznijih tehnickih pitanja za buducnost renderovanja u UE5.
14.10 Kljucni Pojmovi
| Termin | Objasnjenje |
|---|---|
| Ray Tracing | Tehnika renderovanja koja prati zrake svetlosti od kamere kroz scenu za odredjivanje boje piksela. |
| Rasterization | Tehnika renderovanja koja za svaki trougao odredjuje koje piksele pokriva. |
| Primary Ray | Zrak od kamere kroz piksel -- odredjuje primarnu vidljivost. |
| Shadow Ray | Zrak od tacke na povrsini ka svetlu -- odredjuje da li je tacka u senci. |
| Reflection Ray | Zrak u pravcu refleksije od povrsine -- odredjuje boju refleksije. |
| Refraction Ray | Zrak koji se lomi pri prolasku kroz providnu povrsinu (Snell-ov zakon). |
| BVH (Bounding Volume Hierarchy) | Hijerarhijsko stablo ogranicavajucih kutija za brzo nalazenje preseka zraka sa scenom. O(log N) umesto O(N). |
| AABB (Axis-Aligned Bounding Box) | Ogranicavajuca kutija paralelna sa osama koordinatnog sistema. Koristena za cvorove BVH-a. |
| SAH (Surface Area Heuristic) | Heuristika za optimalnu konstrukciju BVH-a, bazirana na povrsini ogranicavajucih kutija. |
| TLAS (Top-Level Acceleration Structure) | Gornji nivo BVH hijerarhije koji sadrzi instance objekata. Rebuild svaki frejm. |
| BLAS (Bottom-Level Acceleration Structure) | Donji nivo BVH hijerarhije koji sadrzi trouglove jednog mesh-a. Gradi se jednom za staticne objekte. |
| Moller-Trumbore | Efikasan algoritam za testiranje preseka zraka sa trouglom. ~27 operacija po testu. |
| RT Core | NVIDIA-ina specijalizovana hardverska jedinica za BVH traversal i ray-triangle intersection. |
| Ray Accelerator | AMD-ova hardverska jedinica za ray-box intersection (RDNA 2/3/4). |
| DXR (DirectX Raytracing) | Microsoft-ov API za ray tracing u DirectX 12. |
| Vulkan RT | Khronos-ov API za ray tracing u Vulkan-u. |
| Ray Generation Shader | Shader koji kreira i ispucava zrake. Polazna tacka RT pipeline-a. |
| Closest-Hit Shader | Shader koji se izvrsava za najblizi pogodak zraka. Ekvivalent pixel shader-a. |
| Any-Hit Shader | Shader za svaki potencijalni pogodak. Koristi se za alpha testing. |
| Miss Shader | Shader koji se izvrsava kada zrak ne pogodi nista. |
| Intersection Shader | Shader za custom geometrijski intersection test (sfere, proceduralne primitive). |
| Shader Binding Table (SBT) | Tabela koja mapira geometriju na odgovarajuce shader-e i resurse. |
| Denoising | Filtriranje sumovite slike dobijene sa malo uzoraka za dobijanje ciste slike. |
| Temporal Denoising | Denoising koji koristi informacije iz prethodnih frejmova za akumulaciju uzoraka. |
| Hybrid Rendering | Kombinacija rasterizacije (za primarnu vidljivost) i ray tracing-a (za sekundarne efekte). |
| SPP (Samples Per Pixel) | Broj zraka/uzoraka po pikselu. Vise = bolji kvalitet, manje suma. |
| SER (Shader Execution Reordering) | NVIDIA hardverska tehnika za grupisanje divergentnih zraka radi bolje koherencije. |
| OMM (Opacity Micromap) | Hardverska tehnika za brzi alpha testing u RT, bez pozivanja any-hit shader-a. |
| Whitted-style RT | Rekurzivni ray tracing sa refleksijama, refrakcijama, i senkama (perfektna refleksija, bez difuzne interrefleksije). |
| Refitting | Azuriranje AABB-ova u postojecem BVH stablu bez promene strukture. Brzo ali degradira kvalitet. |
| Rebuilding | Potpuna ponovna izgradnja BVH stabla. Sporije ali garantuje optimalan kvalitet. |
14.11 Reference i Dalje Citanje
Akademski radovi
- Appel, A. (1968). "Some techniques for shading machine renderings of solids." -- Originalni rad o ray casting-u.
- Whitted, T. (1980). "An improved illumination model for shaded display." -- Rad koji je uveo rekurzivni ray tracing sa refleksijama i refrakcijama.
- Moller, T. & Trumbore, B. (1997). "Fast, Minimum Storage Ray/Triangle Intersection." -- Algoritam koji se i danas koristi za ray-triangle intersection.
- MacDonald, J.D. & Booth, K.S. (1990). "Heuristics for ray tracing using space subdivision." -- Surface Area Heuristic za konstrukciju BVH-a.
- Karras, T. (2012). "Maximizing Parallelism in the Construction of BVHs, Octrees, and k-d Trees." -- Paralelna konstrukcija BVH-a na GPU-u.
- Wyman, C. et al. (2018). "Introduction to DirectX Raytracing." -- Uvodna prezentacija DXR API-ja.
Knjige
- "Ray Tracing in One Weekend" (Peter Shirley) -- Odlican, besplatan uvod u ray tracing. Dostupan online na raytracing.github.io.
- "Ray Tracing Gems" (NVIDIA, ed. Haines & Akenine-Moller) -- Zbirka prakticnih tehnika za real-time ray tracing.
- "Ray Tracing Gems II" (NVIDIA, ed. Marrs, Shirley, Wald) -- Nastavak sa naprednim temama.
- "Physically Based Rendering: From Theory to Implementation" (Pharr, Jakob, Humphreys) -- Kompletna knjiga o PBR i ray tracing-u, dostupna besplatno na pbrt.org.
- "Real-Time Rendering" (Akenine-Moller, Haines, Hoffman) -- Poglavlje o ray tracing-u pokriva hardver i hibridno renderovanje.
Online resursi
- NVIDIA DXR tutorials: developer.nvidia.com/rtx/raytracing -- Prakticni tutoriali za DXR programiranje.
- Vulkan Ray Tracing tutorials: nvpro-samples.github.io/vk_raytracing_tutorial_KHR -- Detaljan vodic za Vulkan RT.
- Epic Games dokumentacija: Zvanicna UE5 dokumentacija za ray tracing podesavanja i best practices.
- NVIDIA GTC prezentacije: Godisnje prezentacije o napretku u RT hardveru i softverskim tehnikama.
- Shadertoy (shadertoy.com) -- Interaktivni primeri ray tracing-a u browser-u.
Unakrsne reference u ovoj knjizi
- Poglavlje 09: Rasterizacija Detaljno -- Detaljan opis rasterizacionog pristupa sa kojim se ray tracing poredi. Edge equations, baricentricne koordinate, depth buffer, overdraw -- koncepti koji imaju direktne analoge u ray tracing-u.
- Poglavlje 12: Globalna Iluminacija -- Path tracing kao "potpuni" ray tracing za GI. Monte Carlo integracija, importance sampling, denoising -- sve tehnike koje se koriste i u real-time RT.
- Poglavlje 25: Lumen -- Kako UE5 kombinuje software tracing i hardware RT u koherentan sistem za real-time GI i refleksije. Prakticna primena koncepata iz ovog poglavlja.
Rezime Poglavlja
Ray tracing je fundamentalno drugacija paradigma od rasterizacije. Umesto da pita "za svaki trougao, koje piksele pokriva?", ray tracing pita "za svaki piksel, koji trougao vidi?" -- i ta razlika otvara vrata fizicki tacnim refleksijama, senkama, globalnoj iluminaciji, i refrakcijama.
U ovom poglavlju smo prosli kroz:
-
Fundamentalnu razliku izmedju rasterizacije i ray tracing-a -- svaki pristup ima svoje prednosti. Rasterizacija je neuporedivo brza za primarnu vidljivost; ray tracing je superioran za sekundarne efekte. Moderno renderovanje koristi oba.
-
Osnovni algoritam ray tracing-a -- od kamere, kroz piksel, do trougla. Whitted-style rekurzivni ray tracing uvodi shadow ray-eve, reflection ray-eve, i refraction ray-eve, omogucavajuci efekte koji su nemogucni u cistoj rasterizaciji.
-
BVH (Bounding Volume Hierarchy) -- akceleraciona struktura koja smanjuje slozenost nalazenja preseka od O(N) na O(log N). Bez BVH-a, ray tracing ne bi bio praktican. SAH heuristika optimizuje kvalitet stabla. TLAS/BLAS dvonivovska hijerarhija omogucava efikasno instancing i dinamicne scene.
-
Moller-Trumbore algoritam -- efikasan ray-triangle intersection test sa samo ~27 operacija, koji kao sporedni proizvod daje baricentricne koordinate za interpolaciju atributa.
-
RT hardver -- NVIDIA RT Core-ovi i AMD Ray Accelerator-i obavljaju BVH traversal i ray-triangle intersection u specijalizovanom hardveru, postizuci 10-100x ubrzanje nad softverskom implementacijom. Svaka generacija hardvera otprilike udvostucuje RT performanse.
-
Hibridno renderovanje -- prakticni pristup koji kombinuje rasterizaciju za base pass sa selektivnim ray tracing-om za senke, refleksije, GI, i AO. Denoising (temporalni i prostorni) omogucava ubedljive rezultate sa manje od 1 zraka po pikselu.
-
DXR/Vulkan RT pipeline -- pet tipova shader-a (ray generation, intersection, any-hit, closest-hit, miss) i Shader Binding Table definisu nelinearan, data-driven pipeline fundamentalno drugaciji od rasterizacionog.
-
Ray tracing u UE5 -- RT refleksije, RT senke, RT GI, RT AO, Lumen hardware RT mode, i Path Tracer. Svaki sa svojim podesavanjima i performansnim implikacijama.
Razumevanje ovih koncepata daje vam alate da donosite informisane odluke o tome kada i kako koristiti ray tracing u vasim UE5 projektima. U Poglavlju 25 cemo videti kako Lumen sve ovo spaja u jedinstven sistem za real-time globalnu iluminaciju.