15 često postavljanih pitanja za Computer Graphics intervju. Pitanja pokrivaju različite nivoe, a odgovore možete vežbati naglas u našem treneru za intervjue.
1Opišite linearno blendovanje omotača (linear blend skinning) i kako se matrice kostiju primenjuju na teme sa višestrukim uticajima.
Linearno blendovanje omotača (Linear Blend Skinning – LBS) predstavlja tehniku geometrijske deformacije koja se koristi za animiranje 3D mreža (meshes) na osnovu prateće hijerarhije skeleta. U skeletnoj animaciji, svaka animirana kost se pomera u odnosu na svoju referentnu konfiguraciju (bind pose). Da bi se transformisalo teme (vertex) na koje utiče više kostiju: 1. Prvobitna pozicija temena u prostoru mreže transformiše se u lokalni prostor svake kosti množenjem inverznom matricom referentne poze kosti ($B_i^{-1}$). 2. Teme se zatim transformiše iz lokalnog prostora kosti u trenutni prostor animirane poze pomoću matrice animirane kosti ($M_i$). Kompozitna transformacija $S_i = M_i \cdot B_i^{-1}$ predstavlja matricu iz palete omotača (skinning palette matrix). 3. Konačna pozicija transformisanog temena računa se kao linearna težinska suma preko svih uticajnih kostiju: $$v' = \sum_{i=1}^{k} w_i \cdot (M_i \cdot B_i^{-1} \cdot v)$$ gde skalarni težinski faktori kostiju $w_i$ moraju biti normalizovani (tj. $\sum w_i = 1.0$). Na GPU-u se ovo obično izvršava u vertex shader-u (ili kroz compute skinning prolaz) preuzimanjem unapred izračunate palete kostiju iz uniformnog ili struktuiranog bafera (uniform/structured buffer) pomoću atributa indeksa kostiju temena, i linearnim blendovanjem pozicija. Normale i tangente temena se transformišu korišćenjem rotacionog dela blendovane matrice i ponovo normalizuju.
#version 450
layout(location = 0) in vec3 inPosition;
layout(location = 1) in vec3 inNormal;
layout(location = 2) in uvec4 inBoneIndices; // Up to 4 bone influences
layout(location = 3) in vec4 inBoneWeights; // Normalized: sum to 1.0
layout(set = 0, binding = 0) uniform BonePalette {
mat4 boneMatrices[128]; // Pre-multiplied: M_i * B_i^-1
};
layout(location = 0) out vec3 outNormal;
void main() {
mat4 skinMatrix = inBoneWeights.x * boneMatrices[inBoneIndices.x] +
inBoneWeights.y * boneMatrices[inBoneIndices.y] +
inBoneWeights.z * boneMatrices[inBoneIndices.z] +
inBoneWeights.w * boneMatrices[inBoneIndices.w];
vec4 skinnedPosition = skinMatrix * vec4(inPosition, 1.0);
gl_Position = u_ViewProjection * skinnedPosition;
// Transform normal with rotational part of skinMatrix and normalize
outNormal = normalize(mat3(skinMatrix) * inNormal);
}
2Opišite GPU (Graphics Processing Unit) instanciranje i objasnite koji podaci po instanci i rasporedi memorije čine iscrtavanje velikog broja sličnih objekata efikasnim.
GPU instanciranje je tehnika iscrtavanja (rendering) koja omogućava crtanje višestrukih kopija iste osnovne geometrije (deleći bafer temena i bafer indeksa) u okviru jednog poziva za crtanje (na primer, `DrawIndexedInstanced` u Direct3D-u ili `glDrawElementsInstanced` u OpenGL-u), čime se drastično smanjuje preopterećenje drajvera između procesora i grafičke kartice (CPU-GPU) i ukupan broj API poziva za crtanje.
Podaci po instanci: Da bi se osiguralo da se instance vizuelno razlikuju i ponašaju različito, obezbeđuju se podaci po instanci, koji obično obuhvataju:
- Podatke o transformaciji: matricu transformacije sveta (world matrix) ili spakovane vrednosti za poziciju, rotaciju i skaliranje.
- Svojstva materijala: nijanse boja, UV pomake/skaliranja ili identifikatore materijala (material ID).
- Dinamičke parametre: fazu animacije, pomake mape osvetljenja (lightmap offsets) ili indikatore vidljivosti.
Rasporedi podataka i metode pristupa:
1. Instancirani baferi temena (Instanced Vertex Buffers): Namenski bafer temena povezan sa frekvencijom koraka po instanci (na primer, `D3D11_INPUT_PER_INSTANCE_DATA`). GPU automatski pomera pokazivač kroz bafer za svaku instancu.
2. StructuredBuffer / Uniform Buffer (SSBO / Constant Buffer): Podaci o instancama se učitavaju u niz unutar bafera, a šejder temena (vertex shader) im pristupa indeksiranjem pomoću ugrađenog sistemskog identifikatora instance (`SV_InstanceID` u HLSL-u, `gl_InstanceID` u GLSL-u).
Održavanje kompaktnih podataka po instanci (na primer, 3x4 afine matrice ili pozicija + kvaternion umesto punih 4x4 matrica, kao i spakovane FP16/uint32 boje) minimizuje propusni opseg GPU memorije i optimizuje korišćenje keš memorije.
3Objasnite frustum culling, occlusion culling i back-face culling i gde se svaki od njih obično izvršava u rendereru.
Frustum culling, occlusion culling i back-face culling su tri komplementarne tehnike određivanja vidljivosti koje odbacuju nevidljive primitive u različitim fazama i na različitim nivoima granularnosti u pajplajnu renderovanja:
1. Frustum Culling: Odbacuje geometriju koja se u potpunosti nalazi van vidnog polja kamere (view frustum). Obično se izvodi na grubim obuhvatnim zapreminama (kao što su AABB ili granične sfere) na CPU-u (Central Processing Unit) pre slanja komandi za iscrtavanje (draw submission), ili na GPU-u (Graphics Processing Unit) putem računskih šejdera (compute shaders) u pajplajnima renderovanja vođenim GPU-om.
2. Occlusion Culling: Odbacuje objekte ili primitive koji su unutar vidnog polja, ali su zaklonjeni drugom neprozirnom geometrijom. Može se izvršiti na CPU-u (korišćenjem softverske rasterizacije ili unapred izračunate vidljivosti) ili na GPU-u (korišćenjem hardverskih occlusion upita, Hi-Z testova bafera dubine u računskim šejderima ili meshlet odbacivanja) pre potpune rasterizacije.
3. Back-Face Culling: Odbacuje pojedinačne poligone čije su normale površina okrenute suprotno od kamere. Ovo tradicionalno automatski obavlja hardver fiksne funkcije tokom postavljanja/rasterizacije trouglova na GPU-u na osnovu redosleda temena u prostoru ekrana (screen-space winding order), mada se može grubo proceniti i na konusima normala klastera (npr. u mesh šejderima).
struct Plane { glm::vec3 normal; float distance; };
struct Sphere { glm::vec3 center; float radius; };
bool isSphereInsideFrustum(const Sphere& sphere, const Plane frustumPlanes[6]) {
for (int i = 0; i < 6; ++i) {
// Signed distance from plane to sphere center
float dist = glm::dot(frustumPlanes[i].normal, sphere.center) + frustumPlanes[i].distance;
if (dist < -sphere.radius) {
return false; // Completely outside
}
}
return true; // Inside or intersecting
}
4Kako splajn (spline) krive kao što su Bézier, B-spline i Catmull-Rom generišu glatke putanje ili geometrijske trake?
Splajn krive pružaju parametarske formulacije $\mathbf{P}(t)$ za definisanje glatkih 3D putanja, kretanja kamere i ekstrudovanih geometrijskih traka (kao što su trake, putevi ili cevi). 1. **Tipovi i svojstva krivi:** - **Bézier krive:** Formulisane pomoću Bernštajnovih (Bernstein) polinoma. One interpolišu samo krajnje tačke; međutačke (kontrolne tačke) definišu tangente. Povezivanje segmenata sa $C^1$ neprekidnošću zahteva kolinearne tangente. - **B-splajn krive (B-splines):** Konstruisane pomoću baznih funkcija preko vektora čvorova (knot vector). Pružaju lokalnu kontrolu i visoku parametarsku neprekidnost ($C^2$ za kubne), ali uglavnom ne prolaze kroz unutrašnje kontrolne tačke. - **Catmull-Rom splajnovi:** Klasa interpolacionih splajnova koji prolaze direktno kroz sve unutrašnje kontrolne tačke automatski obezbeđujući $C^1$ neprekidnost, što ih čini idealnim za putanje koje kreira korisnik. 2. **Generisanje putanja i geometrije:** - Evaluacija splajna u parametru $t$ daje poziciju $\mathbf{P}(t)$ i vektor tangente $\mathbf{T}(t) = \mathbf{P}'(t)$. - Za ekstruziju 3D traka ili cevi, potreban je ortogonalni koordinatni sistem (normala $\mathbf{N}(t)$ i binormala $\mathbf{B}(t)$) duž krive. - Standardni Frene-Sereovi (Frenet-Serret) koordinatni sistemi ne uspevaju ili se naglo obrću u prevojnim tačkama gde je zakrivljenost $\kappa = 0$. Da bi se sprečilo neprirodno uvrtanje trake, **koordinatni sistemi paralelnog transporta (Bishop koordinatni sistemi)** glatko propagiraju referentnu orijentaciju duž krive minimizovanjem rotacione torzije.
5Šta je bafer komandi (command buffer) i zašto je višenitno snimanje komandi važno u naprednim endžinima?
Bafer komandi (ili lista komandi u Direct3D 12) predstavlja strukturu podataka u memoriji u kojoj se grafičke, računske i komande prenosa—kao što su podešavanje stanja pajplajna, vezivanje deskriptora, izdavanje poziva za iscrtavanje (draw calls) i postavljanje barijera pajplajna—snimaju na CPU-u radi naknadnog slanja i asinhronog izvršavanja u GPU redu čekanja. Višenitno snimanje komandi je ključno u naprednim endžinima jer su priprema poziva za iscrtavanje na strani CPU-a, vezivanje stanja i odbacivanje nevidljive geometrije (culling) istorijski bili primarna uska grla. Uklanjanjem ograničenja jednonitnog konteksta, eksplicitni API-ji omogućavaju endžinu da podeli frejm na nezavisne zadatke renderovanja kroz više radnih niti procesora. Na primer, prolazi za senke, segmenti G-bafera i naknadna obrada mogu se snimati istovremeno. Savremeni API-ji ovo podržavaju putem primarnih i sekundarnih bafera komandi (Vulkan) ili listi komandi i paketa (bundles u D3D12). Sekundarni baferi komandi i paketi omogućavaju radnim nitima da snimaju podskupove komandi za iscrtavanje koji se mogu izvršiti unutar primarnog bafera komandi na niti zaduženoj za slanje, čime se maksimizuje iskorišćenost višejezgarnih procesora i minimizuju zastoji u GPU redovima.
6Kako se push konstante ili root konstante razlikuju od uniformnih/konstantnih bafera i kada ih treba koristiti?
Push konstante (u API-ju Vulkan) i root konstante (u DirectX 12) obezbeđuju mehanizam za direktno prosleđivanje malih količina uniformnih podataka unutar samog komandnog bafera ili osnovnog potpisa (root signature), izbegavajući režijske troškove alokacije, ažuriranja i vezivanja GPU resursa bafera podržanih deskriptorima. Nasuprot tome, uniformni baferi (UBO) ili konstantni baferi (CBO) oslanjaju se na namensku alokaciju GPU memorije koja se vezuje za pajplajn preko deskriptora, tabela deskriptora ili skupova deskriptora. Pošto su push/root konstante ugrađene u sam tok komandi, idealne su za podatke visoke frekvencije koji se često menjaju po pojedinačnom iscrtavanju (kao što su matrice transformacije objekata, indeksi materijala/mreža, vremenske vrednosti ili dinamički pomeraji). Međutim, one imaju stroga ograničenja veličine (npr. Vulkan garantuje minimalni limit od samo 128 bajtova, dok je prostor za root signature u D3D12 ograničen na 64 DWORD-a koje dele root deskriptori i tabele). Uniformne/konstantne bafere treba koristiti kada veličina podataka premašuje limite za push konstante, kada se podaci dele kroz više iscrtavanja (kao što su matrice kamere/pogleda po frejmu, globalno osvetljenje scene ili postavke okruženja) ili kada je potrebno perzistentno skladištenje između prolaza.
7Navedite i objasnite koordinatne prostore kroz koje teme (vertex) prolazi od prostora modela do prostora ekrana u rendereru u realnom vremenu.
U grafičkom cevovodu za renderovanje u realnom vremenu, teme (vertex) tipično prolazi kroz nekoliko koordinatnih prostora: prostor modela (lokalni prostor), prostor sveta (World Space), prostor pogleda/kamere (View Space), prostor odsecanja (Clip Space), normalizovane koordinate uređaja (Normalized Device Coordinates - NDC) i prostor ekrana/prozora (Screen/Viewport Space). Teme polazi iz prostora modela u odnosu na lokalni koordinatni početak modela. Množenjem matricom modela/sveta postavlja se i orijentiše u deljeni prostor sveta. Množenjem matricom pogleda transformiše se u prostor pogleda, gde se kamera nalazi u koordinatnom početku i gleda duž standardnog smera posmatranja. Zatim, množenje matricom projekcije transformiše koordinate u 4D prostor odsecanja $(x_c, y_c, z_c, w_c)$, gde se geometrija odseca u odnosu na zapreminu pogleda. Nakon odsecanja, hardver sa fiksnom funkcijom vrši perspektivno deljenje (deljenjem $x_c, y_c, z_c$ sa $w_c$) kako bi se dobile 3D normalizovane koordinate uređaja (NDC). Na kraju, transformacija vidnog polja (Viewport Transform) mapira NDC koordinate u 2D koordinate piksela prostora ekrana i vrednosti bafera dubine.
8Uporedite LBS (Linear Blend Skinning) i DQS (Dual-Quaternion Skinning) u pogledu kvaliteta deformacije, artefakata i inženjerske složenosti.
Linear Blend Skinning (LBS) i Dual-Quaternion Skinning (DQS) predstavljaju dva različita pristupa deformaciji skeletne mreže (mesh): 1. Kvalitet deformacije i artefakti: - LBS izračunava transformisana temena linearnom interpolacijom matrica transformacije kostiju. Iako je brz, LBS pati od gubitka zapremine tokom izraženih rotacija i uvrtanja, pre svega kroz artefakt „omotača bombona“ (candy-wrapper), gde cilindrična geometrija kolabira duž ose uvrtanja. - DQS predstavlja krute transformacije kostiju kao jedinične dualne kvaternione (kombinujući rotaciju i translaciju). Kada se stapaju (npr. korišćenjem Dual Linear Blending), DQS prirodno čuva zapreminu i eliminiše artefakte uvrtanja. Međutim, DQS uvodi sopstvene artefakte, kao što su ispupčenja ili uštinuća pri ekstremnim savijanjima zglobova. 2. Inženjerska složenost i implementacija: - LBS izvorno podržava potpune afine transformacije (translaciju, rotaciju i neuniformno skaliranje ili smicanje) koristeći standardne protočne obrade 4x4 matrica. - DQS izvorno podržava samo krute transformacije. Rukovanje skaliranjem (posebno neuniformnim) zahteva deformaciju u više prolaza, polarnu dekompoziciju ili razdvajanje skaliranja i smicanja. Pored toga, DQS zahteva rešavanje antipodalnosti tokom stapanja (proveru skalarnog proizvoda dualnih kvaterniona radi izbora najkraće putanje rotacije i izbegavanja prevrtanja/kolapsa mreže), što čini matematiku u šejderima i pripremu resursa znatno složenijim.
9Animirani likovi se nepravilno deformišu samo na određenim mrežama (meshes). Koje podatke o resursima (asset) i šejderima (shader) biste pregledali?
Kada se animirani likovi nepravilno deformišu samo na podskupu mreža, problem obično potiče od nepoklapanja podataka u pipeline-u resursa, rasporedu temena (vertex layout) ili konstantama šejdera. Sistematska inspekcija treba da obuhvati:
1. Raspored temena i granice indeksa kostiju: Uverite se da indeksi kostiju po temenu ne premašuju ukupan broj kostiju u skeletu i da ne prelivaju svoj spakovani tip podataka (npr. korišćenje uint8/ubyte4 kada skelet ima više od 256 kostiju, što izaziva prelivanje indeksa).
2. Normalizacija težina kostiju: Proverite da li je zbir težina kostiju po temenu jednak 1.0. Nenormalizovane težine uzrokuju skupljanje temena ka skeletu ili njihovo udaljavanje od njega.
3. Matrice inverzne poze vezivanja (IBM - Inverse Bind Matrices): Potvrdite da matrice inverznog vezivanja mreže odgovaraju pozi mirovanja (rest pose) skeleta i koordinatnom prostoru. Neusklađene poze vezivanja uzrokuju nekontrolisano rastezanje (eksploziju) ili pogrešan pomak mreže.
4. Maksimalan broj uticaja po temenu: Proverite da li je DCC (Digital Content Creation) alat za izvoz eksportovao više uticaja kostiju po temenu (npr. 8 uticaja) nego što raspored bafera temena ili šejder podržavaju (npr. 4 uticaja), čime se odbacuju težine bez ponovne normalizacije.
5. Hijerarhija skeleta i indeksiranje palete: Proverite da li mapiranja indeksa kostiju u mreži odgovaraju paleti matrica kostiju koja se prosleđuje u konstantne/struktuirane bafere.
struct SkinVertex {
float position[3];
uint8_t boneIndices[4];
uint8_t boneWeights[4]; // UNORM8
};
void ValidateMeshSkinData(const std::vector<SkinVertex>& vertices, uint32_t maxBoneCount)
{
for (size_t i = 0; i < vertices.size(); ++i)
{
const auto& v = vertices[i];
int weightSum = 0;
for (int b = 0; b < 4; ++b)
{
assert(v.boneIndices[b] < maxBoneCount && "Bone index exceeds palette size!");
weightSum += v.boneWeights[b];
}
assert(std::abs(weightSum - 255) <= 1 && "Bone weights do not normalize to 1.0!");
}
}
10Šta su morph targets (ili blend shapes) i kako se kombinuju sa skeletnim skinovanjem (skeletal skinning) za animaciju lica?
Morph targets (blend shapes) predstavljaju geometrijske deformacije sačuvane kao pomaci razlike po temenu (delta positions, delta normals i opciono delta tangents) u odnosu na osnovnu mrežu u stanju mirovanja. Svaki morph target se kontroliše skalarnom težinom (obično od 0.0 do 1.0), a deformisani atributi temena se računaju kao: `Morphed_Attribute = Base_Attribute + Sum(Weight_i * Delta_i)`. Pri kombinovanju morph targets sa skeletnim skinovanjem (npr. za animaciju lica): 1. Redosled evaluacije: Razlike (deltas) morph target-a moraju se izračunati u prostoru modela neutralne/vezane poze pre nego što se primeni skeletno skinovanje. 2. Prolaz skinovanja: Deformisane pozicije i normale se zatim transformišu matricama kostiju skeletnog skinovanja. Primena morfovanja pre skinovanja obezbeđuje da se izrazi lica prirodno deformišu pri okretanju glave i rotaciji zglobova vilice. Sa stanovišta performansi i memorijskog propusnog opsega, naivno čuvanje i čitanje punih kopija mreže za desetine blend shape-ova stvara veliko opterećenje memorijske magistrale. Praktične implementacije čuvaju retke razlike (samo temena sa nenultim pomerajem), kompresuju formate razlika (npr. FP16 ili kvantizovane cele brojeve) ili koriste predprolaze GPU računarskih šejdera (compute shader) za jednokratno računanje deformisanih temena pre višestrukih prolaza renderovanja.
struct VertexInput {
float3 position : POSITION;
float3 normal : NORMAL;
uint4 boneIndices : BLENDINDICES;
float4 boneWeights : BLENDWEIGHT;
};
// 1. Accumulate morph deltas in local rest space
float3 morphedPos = input.position;
float3 morphedNorm = input.normal;
for (int i = 0; i < activeMorphCount; ++i) {
morphedPos += morphDeltasPos[i] * morphWeights[i];
morphedNorm += morphDeltasNorm[i] * morphWeights[i];
}
morphedNorm = normalize(morphedNorm);
// 2. Skin morphed geometry to world space
float4 skinnedPos = 0;
float3 skinnedNorm = 0;
for (int b = 0; b < 4; ++b) {
float4x4 boneMat = BoneMatrices[input.boneIndices[b]];
skinnedPos += mul(boneMat, float4(morphedPos, 1.0)) * input.boneWeights[b];
skinnedNorm += mul((float3x3)boneMat, morphedNorm) * input.boneWeights[b];
}
11Objasnite izbor geometrijskog LOD-a (Level of Detail), pojednostavljivanje mreže (mesh simplification) i strategije prelaza koje usklađuju vizuelnu stabilnost, očuvanje atributa i performanse.
Geometrijski nivo detalja (LOD - Level of Detail) optimizuje performanse iscrtavanja smanjenjem složenosti poligonalne mreže kako se objekti udaljavaju od kamere, čime se usklađuju vizuelna vernost i broj slika u sekundi (frame rate).
1. Izbor LOD-a: LOD nivoe treba birati korišćenjem metrika u prostoru ekrana (kao što su projektovani prečnik opisane sfere, procenat visine ekrana ili projektovana greška u pikselima), a ne statičke udaljenosti u svetskom prostoru, kako bi se uzele u obzir promene u vidnom polju (FOV) i rezoluciji kamere. Da bi se sprečilo brzo oscilovanje između LOD nivoa na granicama udaljenosti („LOD thrashing“), primenjuje se histerezis održavanjem zasebnih pragova za prelazak na viši i niži nivo.
2. Pojednostavljivanje mreže: Generisanje van realnog vremena (offline) obično se oslanja na kvadratne metrike greške (QEM - Quadric Error Metrics) putem iterativnog sažimanja ivica (edge collapse). Da bi se očuvao vizuelni kvalitet, algoritmi za pojednostavljivanje moraju očuvati siluete ivica i penalizovati geometrijsku distorziju, kao i očuvati atribute temena (UV šavove, razdvajanja normala, boje temena i težine za animaciju skeleta) uključivanjem članova greške atributa u kvadratnu metriku.
3. Strategije prelaza: Da bi se sprečilo naglo vizuelno pojavljivanje („popping“), grafički endžini koriste:
- Dithered Crossfading / Screen-Door Stippling: Odbacuje piksele u šejderu piksela koristeći prepleteni obrazac diteringa (npr. Bajerovu matricu), glatko pretapajući LOD nivoe bez potrebe za alfa stapanjem ili narušavanjem rane Z-provere (early-Z).
- Geomorfoza (Geomorphing): Interpolira pozicije temena između susednih LOD mreža na grafičkom procesoru (GPU) tokom kratkog vremenskog intervala prelaza.
12Šta je optimizacija bafera indeksa ili keša temena i zašto redosled trouglova utiče na efikasnost keša nakon transformacije?
Optimizacija keša temena (ili bafera indeksa) menja redosled indeksa trouglova i podataka o temenima u mreži kako bi se maksimizovao procenat pogodaka u hardverskim keševima temena na GPU-u (Graphics Processing Unit). GPU-ovi imaju dva glavna keša temena: 1. Keš nakon transformacije (Post-Transform Cache): Mali FIFO/LRU keš koji čuva transformisane izlaze šejdera temena (pozicije, atribute). Kada susedni trouglovi dele temena, njihovo referenciranje u bliskom razmaku unutar toka indeksa omogućava GPU-u da ponovo iskoristi keširane izlaze šejdera umesto da višestruko izvršava šejder temena za isto teme. 2. Keš pre transformacije (Pre-Transform Cache): GPU L1/L2 memorijski keš za sirove podatke bafera temena. Promena redosleda podataka bafera temena tako da odgovara redosledu prvog pristupa optimizovanih indeksa maksimizuje prostornu lokalnost i efikasnost memorijskog protoka. Redosled trouglova direktno određuje redosled pristupa u kešu nakon transformacije. Algoritmi optimizacije (kao što su algoritam Toma Forsajta ili Tipsify) dodeljuju dinamičke ocene ponovnog korišćenja temenima na osnovu valencije i pozicije u kešu, dajući prioritet trouglovima koji kompletiraju preostale reference na nedavno keširana temena kako bi se minimizovao prosečan odnos promašaja keša (ACMR – Average Cache Miss Ratio).
float calculateVertexScore(int cachePosition, int remainingValence) {
if (remainingValence == 0) return -1.0f;
float score = 0.0f;
if (cachePosition >= 0) {
if (cachePosition < 3) {
score = 0.75f; // Recent vertex in cache (bonus for immediate reuse)
} else {
score = std::pow(1.0f - (cachePosition - 3) / 29.0f, 1.5f); // Gradual falloff
}
}
// Bonus for vertices with few remaining triangles (clearing valence faster)
score += 2.0f * std::pow(remainingValence, -0.5f);
return score;
}
13Koji pristupi uklanjanju zaklonjenih objekata (occlusion culling) izbegavaju zastoje između CPU (Central Processing Unit) i GPU (Graphics Processing Unit) jedinica i nepravilno iskakanje objekata?
Tradicionalni hardverski upiti o zaklonjenosti izazivaju asinhrone zastoje pri povratnom čitanju između CPU i GPU jedinica ukoliko CPU čeka rezultate vidljivosti unutar istog frejma. Odlaganje povratnog čitanja za jedan frejm izbegava zastoje, ali uvodi vremensku latenciju, što dovodi do vidljivog iskakanja (popping) kada se novovidljivi objekti ne renderuju odmah. Da bi se izbegli i zastoji između CPU-a i GPU-a i vizuelno iskakanje, moderne produkcione arhitekture koriste:
1. Dvofazno GPU vođeno Hi-Z uklanjanje zaklonjenih objekata: GPU testira granične okvire (bounding boxes) u odnosu na hijerarhijsku Z piramidu dubine (Hi-Z) generisanu iz prethodnog frejma. Objekti za koje se zna da su vidljivi iscrtavaju se u Fazi 1 (generišući početnu dubinu trenutnog frejma). Prethodno zaklonjeni objekti se ponovo testiraju u odnosu na ažurirani Hi-Z bafer trenutnog frejma u Fazi 2; svi novootkriveni objekti se renderuju odmah pre osvetljenja i postprocesiranja, eliminišući iskakanje bez ikakvog povratnog čitanja na CPU.
2. Softverska rasterizacija na CPU-u: Bafer dubine niske rezolucije se rasterizuje isključivo na radnim nitima CPU-a (koristeći SIMD) iz pojednostavljenih geometrija zaklona. CPU testira granične okvire sinhrono bez potrebe za GPU upitima ili latencije prenosa sa GPU-a na CPU.
3. Konzervativne granične zapremine i vremenska histereza: Proširivanje graničnih zapremina ili odlaganje prelaza u nevidljivo stanje sprečava preuranjeno uklanjanje tokom brzog pomeranja kamere.
// Phase 1: Render instances visible in the previous frame
[numthreads(64, 1, 1)]
void Phase1_CullCS(uint id : SV_DispatchThreadID) {
if (id >= totalInstances) return;
Instance inst = instances[id];
if (wasVisibleLastFrame[id] && TestHiZ(inst.bounds, prevFrameHiZ)) {
AppendDraw(phase1DrawBuffer, inst);
currentVisibility[id] = true;
}
}
// [Phase 1 draws -> depth buffer written -> Hi-Z updated for current frame]
// Phase 2: Test previously occluded objects against updated Hi-Z to avoid popping
[numthreads(64, 1, 1)]
void Phase2_CullCS(uint id : SV_DispatchThreadID) {
if (id >= totalInstances) return;
if (!currentVisibility[id] && TestHiZ(instances[id].bounds, currentFrameHiZ)) {
AppendDraw(phase2DrawBuffer, instances[id]);
currentVisibility[id] = true;
}
}
14Opišite tipičan cevovod za obradu resursa od kreirane mreže do GPU (Graphics Processing Unit) bafera u izvršnom okruženju, uključujući generisanje tangenti, kvantizaciju, validaciju i optimizaciju.
Standardni cevovod za obradu resursa transformiše sirove autorske DCC mreže (FBX, glTF, USD) u binarne formate visokih performansi spremne za GPU kroz pet glavnih faza: 1. Prijem i validacija: Izvorna mreža se čisti uklanjanjem dupliranih ili neiskorišćenih temena, odbacivanjem degenerisanih trouglova sa nultom površinom, proverom mnogostrukosti geometrije (manifold geometry), obradom NaN vrednosti i deljenjem višematrijalnih mreža na zasebne podmreže. 2. Generisanje tangentnog prostora: Tangente i bitangente se računaju korišćenjem standardizovanih algoritama (prevashodno MikkTSpace) radi garantovanja vizuelnog poklapanja sa alatima za pečenje normala. Ovo pravilno uzima u obzir UV šavove i preslikane UV mape (pamćenjem orijentacije u tangent.w). 3. Optimizacija: Indeksi se preslaguju radi efikasnosti keša temena nakon transformacije (post-transform vertex cache, npr. Forsyth/Tipsify), baferi temena se preslaguju radi lokalnosti dohvatanja temena pre transformacije, i generišu se nivoi detalja (LOD) ili mrežice (meshlets). 4. Kvantizacija i pakovanje atributa: Atributi temena se kvantizuju radi smanjenja memorijskog otiska i memorijskog protoka: pozicije na 16-bitni half/unorm ili normalizovane cele brojeve, normale i tangente na 8-bitni SNORM ili oktaedarska kodiranja (Oct16/Oct32), a UV koordinate na 16-bitne brojeve sa pokretnim zarezom/unorm. Atributi mogu biti isprepleteni (AoS) ili podeljeni u više tokova (SoA, npr. samo pozicije za prolaze dubine). 5. Priprema i serijalizacija: Baferi, granična tela (AABB/sfere) i LOD tabele se serijalizuju u ravne binarne datoteke koje ne zahtevaju prepravljanje pokazivača tokom izvršavanja, što omogućava brz prenos putem DMA u GPU bafere preko prelazne (staging) memorije.
// Compress a float3 normal into 2D octahedral coordinates (8-bit SNORM each)
vec2 OctEncode(vec3 n) {
n /= (abs(n.x) + abs(n.y) + abs(n.z));
vec2 oct = (n.z >= 0.0) ? n.xy : (1.0 - abs(n.yx)) * sign(n.xy);
return oct * 0.5 + 0.5;
}
// Stored as 2x 8-bit unorm/snorm (2 bytes vs 12 bytes float3)
15Objasnite meshlet strukture, odbacivanje klastera (cluster culling), mesh sejder-e (mesh shaders) i protočne obrade guste mikro-geometrije za velike statičke scene.
Arhitekture protočne obrade zasnovane na meshlet strukturama i gustoj mikro-geometriji (kao što je Unreal Nanite) zamenjuju velike pozive za iscrtavanje sa indeksnim baferima malim, ograničenim klasterima geometrije koji se nazivaju „meshlets”.
1. Meshlet strukture: Meshlet je klaster geometrije obično ograničen na 32–128 temena (vertices) i do 128–256 trouglova. Svaki meshlet sadrži lokalne indekse temena, tokove atributa i unapred izračunate podatke o granicama (graničnu sferu i konus normala).
2. Mesh i amplifikacioni sejderi (Mesh and Amplification Shaders): Oni zamenjuju pipeline fiksne funkcionalnosti za temena, sklapanje primitiva i geometrijske sejdere. Amplifikacioni (Task) sejderi vrše procenu odbacivanja na nivou klastera: odbacivanje van vidnog polja (frustum culling), okluziono odbacivanje (occlusion culling) i odbacivanje zadnjih strana pomoću konusa normala (normal-cone back-face culling) kroz grupe meshlet-a. Preostali meshlet-i pokreću Mesh sejdere, gde grupa niti (threadgroup) kooperativno transformiše temena u deljenoj memoriji na čipu (LDS) i direktno prosleđuje indekse primitiva rasterizatoru.
3. Protočna obrada guste mikro-geometrije: Geometrija visoke gustine proizvodi subpikselne trouglove koji pate od izraženog problema preklapanja piksela unutar rasterizacione jedinice (quad-overdraw, gde standardni hardver rasterizuje pomoćne quad-ove od 2x2 piksela, izvršavajući potpune piksel sejdere za samo 1 pokriveni piksel). Savremeni sistemi za gustu mikro-geometriju koriste hijerarhijske strukture nivoa detalja klastera (DAG) kako bi dinamički birali LOD klastera koji obezbeđuje dužine ivica od približno 1 piksela, i često kombinuju hardversku rasterizaciju za velike poligone sa namenskim računarskim softverskim rasterizatorima za subpikselne mikropoligone.
#define MAX_VERTS 64
#define MAX_PRIMS 128
struct MeshletPayload { uint meshletIndices[32]; };
[outputtopology("triangle")]
[numthreads(32, 1, 1)]
void MainMS(
in uint gtid : SV_GroupThreadID,
in uint gid : SV_GroupID,
in payload MeshletPayload payloadData,
out vertices VertexOutput outVerts[MAX_VERTS],
out indices uint3 outIndices[MAX_PRIMS]
) {
uint meshletId = payloadData.meshletIndices[gid];
Meshlet m = meshlets[meshletId];
SetMeshOutputCounts(m.vertexCount, m.primitiveCount);
// Cooperatively transform vertices
for (uint v = gtid; v < m.vertexCount; v += 32) {
outVerts[v] = TransformVertex(m.vertexOffset + v);
}
// Output local triangle indices
for (uint p = gtid; p < m.primitiveCount; p += 32) {
outIndices[p] = GetMeshletTriangle(m.triangleOffset + p);
}
}