Stiftelser: Representera tredimensionellt utrymme

Innan en enda pixel dras måste en 3D-motor ha ett matematiskt språk för att beskriva objekt i rymden. I kärnan finns ]]] vektorer (trekomponent tuples representerar position, riktning eller färg) och ] makriker ] (4×4 arrays som koda transformationer). I C, dessa definieras vanligtvis som enkla structs:

  • ] för poäng och vektorer.
  • för transformationsmatriser som lagras i kolumn-större ordning.

Varje objekt består av ]trianglar]. En triangel definieras av tre vertiker, var och en med en position och valfritt en normal och färg. Lagra dessa som sammanhängande arrayer (t.ex. ]] för en kub) förbättrar cacheeffektiviteten - en kritisk övervägning när du senare bearbetar tusentals ansikten per ram.

Grafikpipeline: Från vertikaler till pixlar

En grafikpipeline är en sekvens av stadier som omvandlar 3D-scendata till en 2D-bild. I en grundläggande mjukvarurendered motor implementerar du manuellt varje steg. Huvudfaserna är:

  1. ]]Vertex Processing – tillämpning av modell, bild och projektion transformeras.
  2. ] Rasterization – omvandlar förvandlade trianglar till fragment (potentiella pixlar).
  3. ]Shading - datorisera färgen på varje fragment baserat på belysning och material.
  4. Output Merging[] - blandning av fragment med rambufferten, inklusive djuptestning.

Hårdvaruaccelererade motorer (med OpenGL, Vulkan eller DirectX) utför de flesta av dessa steg på GPU, men förståelse av programvarans väg ger dig djup insikt om hur GPU fungerar under huven.

Vertex Processing och transformationer

Varje vertex börjar i modellutrymme (lokala koordinater i förhållande till objektet) för att placera den i världen, tillämpar du en ]modell matrix ] som kodar översättning, rotation och skalning. view matrix omvandlar världskoordinater till kamera-relativa koordinater ([[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]][[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[

I C ser transformationsfunktionerna ut som:

  • - returnerar en identitetsmatris.
  • bygger en översättningsmatris.
  • - rotation runt X-axeln.
  • - multiplicerar en 4×4-matris med en 4-elementsvektor (homogena koordinater).

Vertex-skuggare motsvarande i programvara itererar över alla vertiker, multiplicerar var och en av de kombinerade ] matrisen och lagrar resultatet i en omvandlad vertexbuffert.

Projektion: Perspektiv kontra ortografisk

Projection styr hur djupet representeras på skärmen. ] Perspektiv projektion gör avlägsna objekt verkar mindre, skapa realism. Dess matris är byggd ur synfältet, aspektförhållandet, nära och långt klippning plan. Den klassiska formeln innebär att dela x och y komponenter av z (efter matris multiplikation). I C, du beräkna projektionsmatrisen en gång och återanvända det varje ram:

  • - returnerar en standard perspektivmatris.

]Orthographic projection] bevarar parallella linjer och är enklare, används för UI- eller CAD-verktyg. Det innebär inte perspektivuppdelning.

Rasterization: Fyllande trianglar

Efter projektion har du 2D-skärmskoordinater (som pixelpositioner) och ett djupvärde (z). Rasterization bryter varje triangel i fragment som täcker heltalskoordinater. Den klassiska algoritmen är scanline konvertering :

  1. Sortera triangelns tre vertikaler genom y-koordinat.
  2. Gå ner till vänster och högerkanter, dator x gränser för varje skanlinje.
  3. För varje pixel i den horisontella spannmålet, beräkna fragmentets djup genom att interpolera över triangeln.
  4. Utför ett djuptest ]: jämför fragmentets z med värdet redan i djupbufferten. Om närmare, uppdatera djupbufferten och beräkna fragmentets färg.

Genomföra kant-walking algoritmen effektivt i C kräver täta slingor och noggrann användning av fastpunkt aritmetik för att undvika flytande punkt överhuvudet. Många hobbyistiska motorer börjar med en brute-force-metod (kontrollera varje pixel i gränsrutan) och senare optimera med kantekvationer.

Skuggning och belysning

En enkel platt skuggad motor använder en enda färg per triangel, beräknad från ansiktet normal och en enda ljuskälla. ]]]]Lambertian modell ] ger diffus intensitet: ]]]. I C, du beräknar det normala som korsprodukten av två triangelkanter (efter omvandla vertika till världsrymden), sedan multiplicera ljusintensiteten av triangelns basfärg.

För att lägga till djup, implementera ]Gouraud shading (vertex normals, interpolerade över fragment) eller ]]]] Phong shading ]] (per-pixel normals). Den senare kräver interpolerande världspositioner och normala, sedan datorbelysning per fragment - mer dyrt men dramatiskt bättre resultat.

Bygga motorn i C: Praktiska steg

Låt oss gå igenom att bygga en minimal mjukvara 3D-motor från början. Målet är att göra en roterande kub med omgivande och diffus belysning på ett 640 × 480 pixelfönster.

Steg 1 – Ställ in en pixelbuffert

Definiera en minnesbuffert för ramen och djupet: ] och ]]. Rensa rambufferten till svart och djupbufferten till 1,0 (farplan) varje ram.

Steg 2 – Definiera Scendata

För en kub kräver sex ansikten (12 trianglar) 12 × 3 = 36 vertiker. Förvara dem i en rad strukturer med position (3 flottor) och färg (3 flottor för RGB). Fördefiniera de 8 unika kubhörnen och sex ansiktsfärger. Använd en indexbuffert för att undvika duplicerande vertikaler.

Steg 3 – Förvandlingspipeline

Varje ram, beräkna modellmatrisen från en vinkel (t.ex. rotateY(tid)), vyn matris från en fast kamera tittar på ursprunget, och projektion matrix. Kombinera dem till en enda MVP-matris: ]. Förvandla varje vertex genom att multiplicera med ]. Gör sedan perspektivsprit (dividera x,y,z by w) för att få normaliserade enhetskoordinater (−1 för att hålla skärmsugn: ] ]] ]]]]]] [FLT: [FLT: [FLT: [FLT: ]]]]]]]] [FLT: [FLT: [FLT: [FLT: [FLT: [FLT: [FLT: ]]]]]]

Steg 4 – Rasterisera och skugga

För varje triangel, använd skanlinjemetoden. Medan du går pixlar, interpolera djupet och även interpolera en per-vertex-attribut som världsrymden (för Phong) eller vertexen normal (för Gouraud). Applicera Lambertian diffusa ekvation. Skriv den slutliga färgen till rambufferten om djuptestet passerar.

Steg 5 – Visa

Använd SDL (Simple DirectMedia Layer) eller ett liknande bibliotek för att skapa ett fönster och blita rambufferten. Till exempel, med SDL2: ], ]], ]]] i ]], sedan ]]]] och ]]]] varje ram.

Denna mjukvaruväg går på CPU, så prestanda är begränsad till några tusen trianglar per ram. När du är bekväm kan du ersätta rasteriseringsskiktet med OpenGL, för att överföra det tunga arbetet till GPU samtidigt som du håller din omvandling och scenlogik i C.

Optimera din motor

En basmotor är långsam. Flera optimeringar är avgörande för realtidsprestanda:

  • ]]Back-face culling:[] Skip trianglar vars normaler pekar bort från kameran. Beräkning av det markerade området i den projicerade triangeln (i 2D) eller kontrollera prickprodukten av ansiktet normalt med vyn riktning.
  • Frustumkulling:[] Testa lådan av ett objekt mot de sex planen av utsikten frustum. Om helt utanför, hoppa över hela objektet.
  • Fixed-point arithmetic: Ersätt flytpunktsoperationer med heltalsmatte i täta rasteriseringsslingor. Många motorbyggare använder 16,16 fixerad punkt för scanline interpolering.
  • ] Förvandla cache: Undvik att omvandla samma vertex flera gånger genom att omvandla alla vertikaler av ett nät en gång, sedan med hjälp av index.
  • ]Blockerad rambuffert skriver: Skriv pixlar i minnesordning (vänster-till-höger, top-to-bottom) för att maximera cache-hits.

Dessa tekniker kan driva en mjukvaruåtergivning från 3–5 fps till 60 fps för scener med måttlig komplexitet (t.ex. några hundra trianglar).

Utöka motorn

När en solid grund finns kan du lägga till funktioner stegvis:

  • ]Texturkartläggning:[]] Store 2D-bilder och interpolera UV-koordinater över trianglar. Implementera bilinearfiltrering för smidiga resultat.
  • Avancerad skuggning: Lägg till spekulära höjdpunkter (Blinn-Phong), omgivande ocklusion eller till och med skuggkartor.
  • Scenhantering:] Använd ett oktre eller BSP-träd för att snabbt bestämma vilka objekt som är synliga.
  • Användarinteraktion: Hantera tangentbord och musinmatning för att kretsa kameran eller plocka objekt.
  • Pipeline abstraktion: Designa ett nyare system: definiera vertex och fragmentfunktioner som funktionspekare, vilket möjliggör flexibel rendering utan rekompilering.

Resurser och vidare läsning

För att fördjupa din förståelse, utforska de klassiska texterna och online-handledningarna som inspirerade denna motor:

  • ]Scratchapixel - Fördjupade förklaringar av raytracing, rasterisering och matematik.
  • ]]LearnOpenGL - Moderna OpenGL-handledningar; börja med kapitlet "Hello Triangle".
  • ]Wikipedia: 3D-projektion - Den formella matematiken bakom perspektiv och ortografiska matriser.

Att bygga en 3D-motor i C är en rit av passage för grafikprogrammerare. Det lär dig inte bara hur man skriver effektiv kod men också hur man tänker i tre dimensioner och komponerar prestandakritiska system. Börja med en enda roterande kub, lägg sedan till fler trianglar, texturer och slutligen lämna den till GPU. Resan från rå C till en fullt fungerande motor avslöjar magin bakom varje pixel på skärmen.