Poglavlje 6: Coordinate Spaces i transformacije
U ovom poglavlju pratiš putovanje jednog verteksa od njegovog lokalnog prostora, kroz svet, kroz kameru, sve do piksela na ekranu. Razumećeš zašto postoji toliko koordinatnih prostora, šta se dešava u svakom, i kako MVP matrica objedinjuje ceo proces. Ovo poglavlje je most između matematike (poglavlje 2) i rendering pipeline-a (poglavlje 7).
Zašto postoji više koordinatnih prostora?
Na prvi pogled, moglo bi da izgleda komplikovano — zašto ne možemo da držimo sve u jednom koordinatnom sistemu? Odgovor je isti kao u realnom životu: različiti konteksti zahtevaju različite okvire reference.
Kad opisuješ poziciju svog stola, kažeš "u ćošku sobe" (koordinatni sistem sobe). Kad opisuješ poziciju sobe, kažeš "drugi sprat, levo od stepeništa" (koordinatni sistem zgrade). Kad opisuješ poziciju zgrade, koristiš ulicu i broj (koordinatni sistem grada). Svaki nivo ima svoj koordinatni sistem jer je tako najlakše raditi na tom nivou.
U renderovanju, imamo sličnu hijerarhiju prostora, i svaki postoji iz dobrog razloga:
- Object Space (lokalni prostor) — za definisanje geometrije objekta
- World Space (svetski prostor) — za pozicioniranje u sceni
- View/Camera Space (prostor kamere) — za perspektivu posmatrača
- Clip Space (prostor odsecanja) — za eliminisanje nevidljive geometrije
- NDC (Normalized Device Coordinates) — za normalizaciju na standardni opseg
- Screen Space (prostor ekrana) — za konačne piksele
Vertex prolazi kroz sve ove prostore u jednom frejmu, transformisan matricama na svakom koraku. Hajde da ih prođemo redom.
Object Space (Local Space, Model Space)
Šta je
Object space je koordinatni sistem u kom je 3D model originalno kreiran. Kad modelar pravi stolicu u Blender-u, verteksi stolice su definisani relativno na neko ishodište — obično centar ili baza objekta.
Na primer, stolica može da ima vertex na poziciji (0.5, 0.0, 0.8) u object space-u — to znači 0.5 jedinice udesno od centra stolice, na podu (Y=0), i 0.8 jedinica naviše. Ove koordinate nemaju nikakve veze sa tim gde je stolica u sceni — opisuju samo oblik stolice.
Zašto postoji
Razlog je praktičan: hoćeš da model stolice bude nezavisan od scene. Ista stolica može da se stavi u kuhinju, spavaću sobu, ili na Mesec — njen oblik se ne menja. Menjaju se samo pozicija, rotacija i veličina u sceni — a to je posao transformacija, ne geometrije.
Object space omogućava i instancing — korišćenje istog mesh-a na više mesta. Sto stolica u sceni koristi istih 5.000 verteksa u object space-u, samo sa različitim transformacijama. Bez object space-a, morali bismo da čuvamo 500.000 verteksa (100 kopija × 5.000).
Konvencija ishodišta
Gde je ishodište object space-a? Konvencija zavisi od konteksta:
- Za statične objekte: obično centar ili baza (donji centar) — kad staviš objekat na sto, hoćeš da mu ishodište bude na dnu da pravilno "sedne"
- Za likove: obično baza stopala (da lik stoji na podu) ili centar mase
- Za vozila: obično centar na nivou tla
Pozicija ishodišta utiče na to kako se objekat rotira (rotira se oko ishodišta) i skalira (skalira se od ishodišta). Pogrešno postavljeno ishodište je čest izvor frustracije — objekat se rotira oko pogrešne tačke.
U UE5, ishodište object space-a je pivot point mesh-a. Može se promeniti u DCC alatu pre eksporta.
World Space
Šta je
World space je zajednički koordinatni sistem cele scene. Svaki objekat u sceni ima svoju poziciju, rotaciju i skaliranje u world space-u. Kad kažeš "stol je na poziciji (500, 200, 0) u svetu", to je world space.
Transformacija: Object → World
Da bi došao od object space-a do world space-a, koristiš Model matricu (u UE5 terminologiji: Local to World transform).
Model matrica je 4×4 matrica koja kombinuje:
- Translaciju — gde je objekat u svetu
- Rotaciju — kako je okrenut
- Skaliranje — koliko je velik
Za stolicu koja je na poziciji (500, 200, 0), rotirana 45° oko Z ose, i skalirana za faktor 1.5:
WorldPosition = ModelMatrix × ObjectPosition
Svaki vertex stolice se transformiše ovom matricom da se dobije njegova pozicija u svetu.
World space upotrebe
World space je prostor u kom se dešava većina "logike":
- Fizičke simulacije rade u world space-u
- Osvetljenje se izračunava u world space-u (pozicije svetla i objekata)
- Collision detection radi u world space-u
- AI pathfinding radi u world space-u
Problem velikih svetova
World space ima praktičan problem: preciznost floating-point brojeva (poglavlje 2). Što su koordinate veće (dalje od ishodišta sveta), to je manja preciznost. Na poziciji (0, 0, 0), float ima preciznost od ~0.0000001 metra. Na poziciji (100000, 0, 0) — 100 km od ishodišta — preciznost je oko 0.008 metra (8 mm). Na 1000 km, preciznost je 8 cm.
Za male scene, ovo nije problem. Ali za open-world igre sa svetovima od stotina kilometara, floating-point preciznost postaje ozbiljan izazov. Objekti daleko od ishodišta počinju da "tresu" (jittering), fizika postaje nestabilna, i renderovanje dobija artefakte.
Rešenja:
- World origin rebasing — periodično pomeranje ishodišta sveta blizu kamere, tako da je kamera uvek blizu (0, 0, 0) gde je preciznost najveća
- Large World Coordinates (LWC) — UE5 od verzije 5.0 koristi double-precision (64-bit) za svetske koordinate, ali float (32-bit) za lokalne proračune (shader, rendering)
- Relative rendering — renderovanje relativno na kameru umesto u apsolutnim svetskim koordinatama
View Space (Camera Space, Eye Space)
Šta je
View space je koordinatni sistem definisan kamerom. Kamera je u ishodištu (0, 0, 0), gleda duž jedne ose (obično -Z ili +Z, zavisi od konvencije), i "gore" je duž Y ose.
Zašto postoji
View space postoji zato što za rendering ne zanima nas gde je kamera u svetu — zanima nas gde su objekti relativno na kameru. Iz perspektive kamere, kamera je uvek "tu" a svet se kreće oko nje.
Ovo značajno pojednostavljuje dalje transformacije (projekciju) jer projekcija pretpostavlja da kamera gleda iz ishodišta duž jedne ose.
Transformacija: World → View
View matrica transformiše iz world space-a u view space. View matrica je inverz transformacije kamere.
Ako je kamera na poziciji (10, 5, 3) i rotirana za 30° oko Y ose, view matrica "poništava" tu transformaciju — pomera sve za (-10, -5, -3) i rotira za -30°. Rezultat: kamera je na (0, 0, 0) i gleda "pravo napred".
ViewPosition = ViewMatrix × WorldPosition
Look-at matrica
Čest način konstruisanja view matrice je look-at funkcija:
lookAt(eye, target, up)
gde je:
eye— pozicija kameretarget— tačka na koju kamera gledaup— vektor koji definiše "gore" (obično (0, 1, 0) ili (0, 0, 1))
Iz ova tri parametra, funkcija konstruiše view matricu. Interno:
- Forward vektor: normalize(target - eye)
- Right vektor: normalize(cross(forward, up))
- Up vektor: cross(right, forward) (korigovan)
- Ovi vektori formiraju rotacioni deo matrice
- Negirana pozicija kamere je translacioni deo
Clip Space
Šta je
Clip space je prostor nakon primene projekcione matrice. Ovo je prostor u kom se vrši clipping — eliminisanje geometrije koja je van vidljivog prostora kamere (frustuma).
Projekciona matrica
Projekciona matrica transformiše iz view space-a u clip space. Postoje dva tipa projekcije:
Perspektivna projekcija — simulira ljudski vid: bliži objekti izgledaju veće, dalji manji. Paralelne linije konvergiraju ka tački nedogleda. Ovo je standard za igre i većinu 3D aplikacija.
Ortografska projekcija — nema perspektivu: objekat izgleda isto bez obzira na udaljenost od kamere. Paralelne linije ostaju paralelne. Koristi se za 2D igre, izometrijske prikaze, tehničke crteže, i UI.
Perspektivna projekcija detaljno
Frustum (vidljivi prostor kamere) je zarubljena piramida:
- Near plane — najbliža ravan: geometrija bliža od ovoga se ne renderuje
- Far plane — najdalja ravan: geometrija dalja od ovoga se ne renderuje
- Field of View (FOV) — ugao otvaranja piramide, obično 60-90° horizontalno
- Aspect ratio — odnos širine i visine (16:9, 21:9...)
Perspektivna projekciona matrica (za DirectX / UE5 konvenciju):
| f/aspect 0 0 0 |
| 0 f 0 0 |
| 0 0 far/(far-near) 1 |
| 0 0 -far*near/(far-near) 0 |
gde je f = 1/tan(fov/2)
Ovo izgleda zastrašujuće, ali hajde da razumemo šta radi:
- f/aspect i f skaliraju X i Y koordinate na osnovu FOV-a i aspect ratio-a
- Treći red transformiše Z koordinatu u nelinearnu dubinu (više preciznosti blizu, manje daleko)
- Četvrti red stavlja originalnu Z vrednost u W komponentu — ovo je ključ za perspektivno deljenje
Šta se dešava sa W
U view space-u, W svakog verteksa je 1 (jer su homogene koordinate tačaka). Ali nakon množenja projekcionom matricom, W postaje Z vrednost verteksa u view space-u (udaljenost od kamere). Ovo je ključno za perspektivni efekat.
Clipping
U clip space-u, vertex je vidljiv ako su njegove koordinate unutar:
-W ≤ X ≤ W -W ≤ Y ≤ W 0 ≤ Z ≤ W (DirectX konvencija) ili -W ≤ Z ≤ W (OpenGL)
Trouglovi koji su potpuno van ovih granica se odbacuju (culled). Trouglovi koji su delimično unutar se odsecaju (clipped) — deo trougla van granica se odseče i zameni novim, manjim trouglovima.
Ovo je razlog zašto se ovaj prostor zove "clip space" — tu se dešava clipping.
Normalized Device Coordinates (NDC)
Perspektivno deljenje
Posle clippinga, vrši se perspektivno deljenje — svaka koordinata se deli sa W:
NDC_X = Clip_X / Clip_W
NDC_Y = Clip_Y / Clip_W
NDC_Z = Clip_Z / Clip_W
Pošto je W = Z (udaljenost od kamere), deljenje sa W čini da dalji objekti imaju manje X i Y koordinate — oni "šrinkaju" sa udaljenošću. Ovo je perspektivni efekat — osnova svakog realističnog 3D prikaza.
NDC opseg
Nakon perspektivnog deljenja, koordinate su u Normalized Device Coordinates:
- X: [-1, 1] (levo do desno)
- Y: [-1, 1] (dole do gore)
- Z: [0, 1] (DirectX: near do far) ili [-1, 1] (OpenGL)
NDC je "normalizovan" — iste koordinate bez obzira na rezoluciju ekrana, aspect ratio, ili FOV. (0, 0) je centar ekrana, (-1, -1) je donji levi ugao, (1, 1) je gornji desni.
Screen Space (Viewport Transform)
Transformacija: NDC → pikseli
Konačno, NDC koordinate se transformišu u screen space — konkretne piksel koordinate na ekranu:
Screen_X = (NDC_X + 1) / 2 × ScreenWidth
Screen_Y = (1 - NDC_Y) / 2 × ScreenHeight (Y se invertuje!)
Na Full HD ekranu (1920×1080):
- NDC (-1, -1) → Screen (0, 1080) — donji levi ugao
- NDC (0, 0) → Screen (960, 540) — centar
- NDC (1, 1) → Screen (1920, 0) — gornji desni ugao
Z koordinata se čuva za depth buffer (Z-buffer) — koristi se za određivanje koji objekat je ispred kog.
Viewport
Viewport definiše koji deo ekrana koristi za renderovanje. Obično pokriva ceo ekran, ali može da bude i manji — na primer, za split-screen multiplayer, svaki igrač ima svoj viewport koji pokriva polovinu ekrana.
MVP matrica — sve u jednom
Pošto su sve transformacije matrice, mogu se kombinovati u jednu:
MVP = Projection × View × Model
Ova jedna matrica transformiše vertex direktno iz object space-a u clip space:
ClipPosition = MVP × ObjectPosition
GPU ovo radi u vertex shaderu — za svaki vertex, pomnoži ga MVP matricom. Rezultat je pozicija u clip space-u, odakle hardware nastavlja sa clippingom, perspektivnim deljenjem, i rasterizacijom.
Zašto je MVP efikasan
Umesto tri odvojene transformacije (3 × matrica-vektor množenja po verteksu), kombinujemo matrice unapred (matrica-matrica množenje, jednom po objektu) i primenjujemo jednu kombinovanu matricu na svaki vertex.
Za objekat sa 10.000 verteksa:
- Odvojeno: 3 matrica-vektor × 10.000 = 30.000 množenja
- MVP: 1 matrica-matrica (za pripremu) + 1 matrica-vektor × 10.000 = 10.001 množenja
Skoro 3× manje posla. A Model matrica se računla jednom po objektu (ne po vertexu), View i Projection jednom po frejmu (ne po objektu). Engine obično prosleđuje Model, View, i Projection odvojeno u shader, a shader ih kombinuje ili koristi zasebno zavisno od potrebe.
Zašto shader ponekad treba odvojene matrice?
Vertex shader obično računa i druge stvari osim pozicije:
- World position — potrebna za osvetljenje, fog, distance-based efekte → treba Model matrica
- View direction — pravac od verteksa ka kameri → treba View matrica ili pozicija kamere u world space-u
- Normale — transformišu se samo Model matricom (bez View i Projection) i to inverznom transponovanom
Zato shader obično prima odvojene matrice i koristi ih kako treba za svaku namenu.
Specijalni prostori
Pored glavnih prostora u pipeline-u, postoje i specijalizovani prostori koji se koriste u specifičnim situacijama:
Tangent Space
Tangent space je lokalni koordinatni sistem definisan na površini mesh-a, u svakom verteksu:
- Normal (N) — okomit na površinu
- Tangent (T) — u pravcu U koordinate UV mape
- Bitangent (B) — u pravcu V koordinate, okomit na N i T
Tangent space se koristi za normal mapping — normal mapa čuva perturbacije normala u tangent space-u, jer su tako nezavisne od orijentacije objekta u svetu. Detalji u poglavlju 19.
TBN matrica (Tangent-Bitangent-Normal) je 3×3 matrica koja transformiše iz tangent space-a u world space (ili view space).
Light Space
Light space je koordinatni sistem iz perspektive svetla — koristi se za shadow mapping. Scena se renderuje iz ugla svetla, i rezultat (depth buffer) je shadow map. Detalji u poglavlju 13.
Texture Space
UV prostor — 2D prostor gde su definisane UV koordinate. Transformacije u texture space-u (skaliranje, offset, rotacija UV-a) kontrolišu kako se tekstura prikazuje na površini.
Depth Buffer i Z vrednosti
Šta je depth buffer
Depth buffer (Z-buffer) je tekstura (render target) iste rezolucije kao framebuffer koja čuva dubinu svakog piksela — udaljenost od kamere. Koristi se za rešavanje visibility problem-a — koji objekat je ispred kog.
Kad se novi piksel renderuje, njegova Z vrednost se poredi sa vrednošću u depth bufferu:
- Ako je bliži kameri (manja Z): nacrtaj piksel i ažuriraj depth buffer
- Ako je dalji (veća Z): odbaci piksel (zaklonjen je)
Nelinearnost depth buffer-a
Depth buffer ne čuva linearnu udaljenost od kamere. Zbog perspektivne projekcije, Z vrednosti su nelinearne — mnogo više preciznosti je koncentrisano blizu near plane-a, i sve manje prema far plane-u.
Za tipičan setup (near=0.1, far=10000):
- Prvih 50% depth buffer preciznosti pokriva samo prvih ~1 metar od kamere
- Preostalih 50% pokriva 9999 metara
Ovo znači da se Z-fighting (vizuelni artefakt gde se dva bliska objekta "bore" za isti piksel) dešava mnogo češće na velikim udaljenostima.
Reverse-Z
Reverse-Z je tehnika gde se depth buffer obrne:
- Near plane → Z = 1.0
- Far plane → Z = 0.0
Ovo koristi činjenicu da floating-point brojevi imaju veću preciznost blizu 0 nego blizu 1. Sa reverse-Z:
- Blizu kamere (Z blizu 1.0): floating-point preciznost je razumna
- Daleko od kamere (Z blizu 0.0): floating-point preciznost je maksimalna!
Rezultat: mnogo ravnomernija distribucija preciznosti i dramatično manje Z-fighting-a na velikim udaljenostima.
UE5 koristi reverse-Z sa infinite far plane — far plane je efektivno na beskonačnoj udaljenosti, a preciznost je i dalje dovoljna zahvaljujući reverse-Z.
Praktične implikacije za razvoj
Near plane i problemi
Near plane (najbliža ravan klipinga) je delikatan parametar:
- Premali near plane (npr. 0.01) — više depth buffer preciznosti se troši na prvih par centimetara, uzrokujući Z-fighting na udaljenosti
- Preveliki near plane (npr. 10) — objekti bliže od 10 jedinica se odsecaju, što može da bude vidljivo (posebno oružje u FPS igrama)
UE5 default near plane je 10 cm (0.1 metra), što je dobar balans za većinu scenarija.
FOV i percepcija
Field of View (FOV) dramatično utiče na percepciju prostora:
- Mali FOV (npr. 60°) — "zoom" efekat, prostor izgleda sabijeno, objekti izgledaju bliže
- Veliki FOV (npr. 120°) — "wide angle" efekat, prostor izgleda rastegtnut, objekti na ivicama su distorzirani
- Tipičan FOV za igre: 75-100° horizontalno
VR zahteva FOV blizu prirodnog ljudskog vida (~110°) da bi bio uverljiv.
Aspect ratio
Aspect ratio (odnos širine i visine) utiče na projekcionu matricu:
- 16:9 — standard za monitore i TV
- 21:9 — ultrawide monitori
- 4:3 — stari standard, retko se koristi
Engine obično automatski prilagođava horizontalni ili vertikalni FOV na osnovu aspect ratio-a. Tipičan pristup: fiksiraj vertikalni FOV, prilagodi horizontalni po aspect ratio-u (tako da širi monitori vide više sveta levo-desno, ali istu visinu).
Rezime transformacije jednog verteksa
Hajde da pratimo jedan vertex kroz ceo pipeline:
1. Object Space: Vertex stolice: (0.5, 0.8, 0.0, 1.0)
2. Object → World (Model matrica): Stolica je na poziciji (500, 200, 0), rotirana 45° oko Z ose. WorldPos = ModelMatrix × (0.5, 0.8, 0.0, 1.0) = (500.14, 200.92, 0.0, 1.0)
3. World → View (View matrica): Kamera je na (510, 195, 1.7), gleda ka stolici. ViewPos = ViewMatrix × WorldPos = (-3.2, 0.5, -12.4, 1.0) (vertex je 12.4 jedinica ispred kamere, malo levo i gore)
4. View → Clip (Projection matrica): Perspektivna projekcija, FOV=90°, aspect=16:9. ClipPos = ProjMatrix × ViewPos = (-4.6, 0.9, 11.8, 12.4) (W = 12.4, što je originalna Z udaljenost)
5. Perspektivno deljenje (Clip → NDC): NDC = ClipPos / W = (-0.37, 0.07, 0.95) (malo levo od centra, malo gore, blizu far plane-a)
6. NDC → Screen (Viewport transform): Ekran 1920×1080. ScreenX = (-0.37 + 1) / 2 × 1920 = 604 piksela ScreenY = (1 - 0.07) / 2 × 1080 = 502 piksela ScreenZ = 0.95 (za depth buffer)
Vertex stolice se prikazuje na pikselu (604, 502), sa dubinom 0.95.
Rezime ključnih pojmova
| Pojam | Značenje |
|---|---|
| Object Space | Lokalni koordinatni sistem objekta |
| World Space | Zajednički koordinatni sistem scene |
| View Space | Koordinatni sistem kamere |
| Clip Space | Prostor nakon projekcije, pre perspektivnog deljenja |
| NDC | Normalized Device Coordinates — normalizovane koordinate [-1,1] |
| Screen Space | Koordinate piksela na ekranu |
| Model matrica | Object → World transformacija |
| View matrica | World → View transformacija (inverz kamere) |
| Projection matrica | View → Clip transformacija (perspektiva ili ortografska) |
| MVP matrica | Kombinovana Projection × View × Model |
| Frustum | Vidljivi prostor kamere (zarubljena piramida) |
| Clipping | Eliminisanje/odsecanje geometrije van frustuma |
| Perspektivno deljenje | Deljenje sa W — daje perspektivni efekat |
| Near/Far plane | Najbliža/najdalja ravan vidljivosti kamere |
| FOV | Field of View — ugao otvaranja kamere |
| Depth buffer | Tekstura koja čuva dubinu svakog piksela |
| Reverse-Z | Obrnuti depth buffer za bolju preciznost na daljini |
| Tangent space | Lokalni prostor površine (N, T, B) za normal mapping |
Sledeće poglavlje daje pregled celog render pipeline-a — od vertex shadera do finalnog piksela. Sad kad razumeš prostore i transformacije, vreme je da vidiš kako GPU obrađuje geometriju korak po korak.
📖 Dalje čitanje: