Table of Contents
Fundaţii: Reprezentarea spaţiului tridimensional
Înainte de a fi desenat un singur pixel, un motor 3D trebuie să aibă o limbă matematică pentru a descrie obiectele în spațiu. La nucleu sunt vectori (trei tuple componente reprezentând poziția, direcția, sau culoarea) și matrice (4×4 array-uri care codifică transformările). În C, acestea sunt definite de obicei ca structuri simple:
- pentru puncte și vectori.
- pentru matricele de transformare stocate în ordine majoră coloană.
Fiecare obiect este compus din triunghiuri. Un triunghi este definit de trei vertice, fiecare cu o poziție, și opțional o normalitate și culoare. Păstrarea acestora ca array-uri contiguous (de exemplu, ] pentru un cub îmbunătățește eficiența cache-ului ți-o atenție critică atunci când procesezi mai târziu mii de fețe pe cadru.
Conducta grafică: de la Vertices la Pixels
O conducta grafica este o secventa de etape care transforma datele de scena 3D intr-o imagine 2D. Într-un motor de baza, se implementeaza manual fiecare etapa. Principalele faze sunt:
- Vertex Processing
- Rasterizare
- Shading
- A se vedea tabelul de mai jos.
Motoarele accelerate hardware (folosind OpenGL, Vulkan, sau DirectX) efectuează majoritatea acestor pași pe GPU, dar înțelegerea traseului software vă oferă o înțelegere profundă a modului în care funcționează GPU sub capotă.
Procesarea și transformarea Vertex
Fiecare vertex începe în model spaţiu[ (coordinate locale relative la obiect). Pentru a-l poziţiona în lume, aplicaţi o model matrice[ care codifică traducerea, rotaţia şi scalarea. Apoi matriţa de vizualizare[ transformă coordonatele lumii în coordonate de vizualizare a camerei (spaţiu vizualizat). În cele din urmă, matricea de proiectie hărţile vizualizează spaţiul către un sistem de coordonate normalizat [] spaţiu de clipire) unde diviziunea de perspectivă şi cartografierea de vizualizare produc coordonate ale ecranului.
În C, funcţiile de transformare arată ca:
- [ ]
Echivalentul vertex nuamer în software iterează peste toate verticele, multiplică fiecare prin matricea combinată și stochează rezultatul într-un tampon vertex transformat.
Proiecţie: Perspectivă versus Ortografie
Proiecţia controlează modul în care adâncimea este reprezentată pe ecran. Proiecţia perceptivă face ca obiectele îndepărtate să apară mai mici, creând realism. Matricea sa este construită din câmpul vizual, raportul de aspect, din planurile de tăiere apropiate şi îndepărtate.Forma clasică implică împărţirea componentelor x şi y la z (după multiplicarea matricei). În C, calculezi matricea de proiecţie o dată şi reutilizezi fiecare cadru:
Proiecţia ortografică păstrează linii paralele şi este mai simplă, folosită pentru instrumentele UI sau CAD. Nu implică divizarea perspectivelor.
Rasterizare: Triunghiuri de umplere
După proiecție, aveți coordonate de ecran 2D (ca poziții de pixel) și o valoare de adâncime (z). Rasterizarea rupe fiecare triunghi în fragmente care acoperă coordonatele pixelului întreg. Algoritmul clasic este conversie de scanlină:
- Sortează triunghiul trei versuri cu y-coordonat.
- Mergi pe marginile stângi şi drepte, calculând limitele x pentru fiecare linie de scanare.
- Pentru fiecare pixel din lungimea orizontală, se calculează adâncimea fragmentului de țigară prin interpolarea triunghiului.
- Efectuați un test aprofundat: comparați fragmentul
Punerea în aplicare algoritmul de mers pe jos de margine eficient în C necesită bucle strânse și utilizarea atentă a aritmetica punct fix pentru a evita plutire-punct deasupra capului. Multe motoare hobby începe cu o abordare brută-forță (verificarea fiecare pixel în caseta de legare) și mai târziu optimizați cu ecuații margine.
Umbră şi iluminare
Un motor simplu cu umbră plată utilizează o singură culoare pe triunghi, calculat din fața normală și o singură sursă de lumină. Modelul Lambertian oferă o intensitate difuză: . În C, calculezi normalul ca produs transversal al două margini triunghiale (după transformarea verticelor în spațiul mondial), apoi multiplică intensitatea luminii cu culoarea de bază triunghiuri.
Pentru a adăuga adâncime, se pune în aplicare Gouraud umbrare[] (vertex normals, interpolate peste fragmente) sau PHong umbrare (per-pixel normals). Aceasta din urmă necesită interpolarea pozițiilor și a normalilor din lume, apoi iluminatul de calcul pe fragment mai scump, dar rezultate dramatic mai bune.
Construirea motorului în C: etape practice
Let
Pasul 1
Defineşte un tampon de memorie pentru cadru şi adâncime: şi . Eliberează suportul de cadru pentru negru şi tamponul de adâncime pentru 1.0 (planul îndepărtat) fiecare cadru.
Etapa 2
Pentru un cub, șase fețe (12 triunghiuri) necesită 12×3 = 36 de vertice. Păstrați-le într-o serie de structuri cu poziție (3 flotoare) și culoare (3 flotoare pentru RGB). Pre-definește cele 8 colțuri unice cubice și șase culori ale feței. Utilizați un tampon index pentru a evita duplicatul verticelor.
Pasul 3
Fiecare cadru, calculaţi matricea modelului dintr-un unghi (de exemplu, rotiţi Y (timp)), matricea de vizualizare dintr-o cameră fixă care priveşte originea şi matricea de proiecţie. Combină-le într-o singură matrice MVP: . Transformaţi fiecare vertex prin înmulţire cu . Apoi efectuaţi o împărţire a perspectivei (divide x,y,z by w) pentru a obţine coordonate normalizate ale dispozitivului (−1 la +1). Harta la coordonatele ecranului: , (y inversat). Păstraţi NDC z pentru testarea adâncimii.
Pasul 4
Pentru fiecare triunghi, utilizați metoda scanline. În timp ce mersul pe jos pixeli, Interpolați adâncimea și, de asemenea, interpolați un atribut per-vertex ca poziția lumii-spațiu (pentru Phong) sau vertex normal (pentru Gouraud). Aplicați ecuația difuză Lambertian. Scrieți culoarea finală la cadrubuffer în cazul în care testul de adâncime trece.
Pasul 5
Utilizați SDL (Simplu DirectMedia Layer) sau o bibliotecă similară pentru a crea o fereastră și a arunca în aer bufferul de cadru. De exemplu, cu SDL2: , , ] în , apoi și fiecare cadru.
Această cale software rulează pe CPU, astfel încât performanța este limitată la câteva mii de triunghiuri pe cadru. Odată confortabil, puteți înlocui stratul de rasterizare cu OpenGL, descărcarea de lucru grele la GPU în timp ce menținerea de transformare și logica scenei în C.
Optimizarea motorului
Un motor de bază este lent. Mai multe optimizări sunt esențiale pentru performanța în timp real:
- Umplere pe partea din spate a feței: Săriți triunghiurile ale căror normali se îndepărtează de cameră. Calculați zona semnată a triunghiului proiectat (în 2D) sau verificați produsul punctat al feței normale cu direcția de vizualizare.
- Testați cutia de legare a unui obiect împotriva celor șase planuri de frest de vedere. Dacă complet în afara, săriți peste întregul obiect.
- Aritmetica punct fix: Înlocuiți operațiunile cu punct plutitor cu matematica totală în buclele de rasterizare strânse. Mulți constructori de motoare folosesc 16.16 punct fix pentru interpolarea liniei de scanare.
- Pre-transform cache: Evitați transformarea aceluiași vertex de mai multe ori prin transformarea tuturor verticelor unei ochiuri odată, apoi folosind indici.
- [ ] Blocked cadru tampon scrie:[ Scrie pixeli în memorie-ordine (stânga-la-dreapta, de sus-la-bottom) pentru a maximiza hit-uri cache.
Aceste tehnici pot împinge un producător de software de la 3
Extinderea motorului
Odată ce o fundație solidă există, puteți adăuga caracteristici treptat:
- Maparea textului: Depozitați imagini 2D și interpolați coordonatele UV peste triunghiuri. Implementați filtrarea biliniară pentru rezultate netede.
- Umbrare avansată: Adăugați elemente de referință speculante (Blinn-Phong), ocluzia ambientală sau chiar hărțile umbrei.
- Managementul de scenă: Utilizați un octee sau arbore BSP pentru a determina rapid ce obiecte sunt vizibile.
- Interacțiunea cu utilizatorul: Manipulați tastatura și intrarea mouse-ului pentru a orbita camera sau a alege obiecte.
- Abstractare de pipelină: Proiectarea unui sistem de umbrire: definirea funcţiilor vertex şi fragment ca puncte de funcţionare, permiţând redarea flexibilă fără recompilare.
Resurse şi lectură în continuare
Pentru aprofundarea înțelegerii dvs., explora textele clasice și tutoriale online care au inspirat acest motor:
Construirea unui motor 3D în C este un ritual de trecere pentru programatori grafică. Ea vă învață nu numai cum să scrieți un cod eficient, dar și cum să gândiți în trei dimensiuni și să compuneți sisteme critice de performanță. Începeți cu un singur cub rotativ, apoi adăugați mai multe triunghiuri, texturi, și în cele din urmă să-l predați GPU. Călătoria de la C brut la un motor complet funcțional dezvăluie magia din spatele fiecărui pixel de pe ecran.