Table of Contents
Stiftelser: Representere tredimensjonell plass
Før en enkelt piksel tegnes, må en 3D-motor ha et matematisk språk for å beskrive objekter i rommet. I kjernen er VVS (trekomponent tupler som representerer posisjon, retning eller farge) og matriser (4×4 tabeller som koder transformasjoner). I C er disse typisk definert som enkle strukturer:
- for punkt og vektorer.
- for transformasjonsmatriser lagret i kolonne-major rekkefølge.
Hvert objekt består av triangler. En trekant er definert av tre hjørner, hver med en posisjon, og valgfritt en normal og farge. Lagring av disse som sammenhengende arrays (f.eks. for en kube) forbedrer cache effektivitet ⁇ en kritisk vurdering når du senere behandler tusenvis av ansikter per ramme.
Graphics Pipeline: Fra hjørner til Pixels
En grafikkrørledning er en sekvens av stadier som forvandler 3D scenedata til et 2D-bilde. I en grunnleggende programvare-fornyet motor, implementerer du manuelt hvert trinn. Hovedfasene er:
- Vertexprosessering ⁇ bruk av modell, visning og projeksjonstransformasjoner.
- Rastering ⁇ omforming av transformerte trekanter til fragmenter (potensielle piksler).
- Shading ⁇ å beregne fargen på hvert fragment basert på belysning og materiale.
- Output Merging ⁇ blandingsfragmenter med rammebufferen, inkludert dybdetesting.
Maskinvareakselererte motorer (ved hjelp av OpenGL, Vulkan eller DirectX) utfører de fleste av disse trinnene på GPU, men å forstå programvarestien gir deg dyp innsikt i hvordan GPU fungerer under hetten.
Vertex prosessering og transformasjoner
Hver hjørne starter i modelrom (lokale koordinater i forhold til objektet). For å plassere det i verden, bruker du en modelmatrise] som koder oversettelse, rotasjon og skalering. Deretter ] forvandler verdenskoordinatene til kamerarelasjon (vis rom). Til slutt, ] kartvisningsplass til et normalisert koordinatsystem (]clip space]] der perspektivdivisjon og visningskartlegging produserer skjermkoordinater.
I C ser transformasjonsfunksjonene ut som:
- ⁇ returnerer en identitetsmatrise.
- ⁇ bygger en oversettelsesmatrise.
- ⁇ rotasjon rundt X-aksen.
- ⁇ multipliserer en 4×4-matrise ved hjelp av en 4-element vektor (homogene koordinater).
Vertex-shader-ekvivalenten i programvare iterater over alle virvelløse, multipliserer hver av den kombinerte matrisen, og lagrer resultatet i en transformert virvelbuffer.
Prosjektion: Perspektiv mot Ortografisk
Prosjektion styrer hvor dypt det er representert på skjermen. Perspektiv projeksjon gjør fjerne objekter synes mindre, skaper realisme. Matrisen er bygget fra synsfeltet, aspektforholdet, nær og langt klippeplan. Den klassiske formelen innebærer å dele x og y-komponentene med z (etter matrisemultiplikasjonen). I C beregner du projeksjonsmatrisen én gang og gjenbruker den hver ramme:
- ⁇ returnerer en standard perspektivmatrise.
Ortografiske fremspring bevarer parallelle linjer og er enklere, brukt til UI- eller CAD-verktøy. Det involverer ikke perspektivdeling.
Rasterisering: Fyllende trekanter
Etter projeksjon har du 2D-skjermkoordinater (som pikselposisjoner) og en dybdeverdi (z). Rasterisering bryter hver trekant til fragmenter som dekker heltalspikselkoordinater. Den klassiske algoritmen er scanline konvertering:
- Sorter trekantens tre hjørner etter y-koordinat.
- Gå ned til venstre og høyre kanter, og beregn x grenser for hver skannelinje.
- For hver piksel i horisontalt spenn, beregne fragmentets dybde ved å interpolere over trekanten.
- Utfør en dybdetest: sammenlikn fragmentets z med verdien allerede i dybdebufferen. Hvis nærmere, oppdater dybdebufferen og beregne fragmentets farge.
Implementere kant-gang algoritmen effektivt i C krever tette løkker og forsiktig bruk av fast punkt aritmetikk for å unngå flytende punkt overhead. Mange hobbyistiske motorer starter med en brute-kraft tilnærming (sjekker hver piksel i avgrensende boks) og senere optimalisere med kantligninger.
Skygge og belysning
En enkel flatskjermmotor bruker en enkelt farge per trekant, beregnet fra ansiktets normale og en enkelt lyskilde. Lambertian-modellen gir diffus intensitet: . I C beregner du det normale som tverrproduktet av to trekantkanter (etter å ha forvandlet hjørner til verdensrommet), og multipliserer deretter lysintensiteten med trekantens basisfarge.
For å legge til dybden, implementer Gouraud shading (vertex normals, interpolert på tvers av fragmenter) eller Phong shading] (per-piksel normaler). Sistnevnte krever interpolerende verdensposisjoner og normaler, deretter databehandling per fragment ⁇ dyrere men dramatisk bedre resultater.
Bygge motoren i C: Praktiske trinn
La oss gå gjennom å bygge en minimal programvare 3D-motor fra grunnen. Målet er å gjøre en roterende kube med omgivelses- og diffus belysning til et 640×480 pikselvindu.
Trinn 1 ⁇ Sett opp en Pixel Buffer
Definer en minnebuffer for rammen og dybden: og . Tøm rammen til svart og dybdebufferen til 1,0 (farplan) hver ramme.
Trinn 2 ⁇ Definere Scenedata
For en kube krever seks ansikter (12 trekanter) 12×3 = 36 hjørner. Oppbevar dem i en rekke strukturer med posisjon (3 flyter) og farge (3 flyter for RGB). Forut de 8 unike kube hjørner og seks ansiktsfarger. Bruk en indeksbuffer for å unngå å duplisere hjørner.
Trinn 3 ⁇ Transformasjon Pipeline
Hver ramme, beregne modellmatrisen fra en vinkel (f.eks. roterer Y(tid)), visningsmatrisen fra et fast kamera som ser på opprinnelsen, og projeksjonsmatrisen. Kombiner dem til en enkelt MVP-matrise: . Transformer hver hjørne ved å multiplisere med . Deretter utføre perspektivdeling (dividde x,y,z ved w) for å få normalisert enhetskoordinater ( ⁇ 1 til +1). Kart til skjermkoordinater: , (y invertert).
Trinn 4 ⁇ Rasterize og Shade
For hver trekant, bruk skannelinjen metoden. Mens du går piksler, interpoler dybden og også interpolerer en per-vertex-attributt som verdensrommet posisjon (for Phong) eller hjørne normal (for Gouraud). Påfør Lambertian diffuse ligning. Skriv den endelige fargen til rammebufferen hvis dybdetesten passerer.
Trinn 5 ⁇ Vis
Bruk SDL (Simple DirectMedia Layer) eller et lignende bibliotek for å opprette et vindu og blit rammebuffer. For eksempel, med SDL2: , ], i ], deretter og hver ramme.
Denne programvarestien kjører på CPU, så ytelsen er begrenset til noen tusen trekanter per ramme. Når du er komfortabel, kan du erstatte rastiseringslaget med OpenGL, fralaste det tunge arbeidet til GPU mens du holder transformasjonen og scenelogikken i C.
Optimerer motoren din
En grunnleggende motor er langsom. Flere optimeringer er avgjørende for ytelse i sanntid:
- Baksiden killing: Hopp over trekanter som normalt peker bort fra kameraet. Beregn det signerte området til den projiserte trekanten (i 2D) eller sjekk prikkproduktet til ansiktet normal med visningsretningen.
- Frustum culling: Test avgrenser boksen av et objekt mot de seks flyene i visningen frustum. Hvis helt utenfor, hoppe over hele objektet.
- Fixed-punkt aritmetisk: erstatte flytende punkt operasjoner med heltalsmat i stramme rastiseringssløyfer. Mange motorbyggere bruker 16,16 fast punkt for scanline interpolasjon.
- Unngå å forvandle samme hjørne flere ganger ved å forvandle alle hjørner av en mesh én gang, og deretter bruke indekser.
- Blocked rammebuffer skriver: Skriv piksler i minne- rekkefølge (venstre-til-høyre, topp-til-botn) for å maksimere cache hits.
Disse teknikkene kan presse en programvare som gjengiver fra 3-5 fps til 60 fps for scener av moderat kompleksitet (f.eks. noen hundre trekanter).
Forlenge motoren
Når det finnes et solid fundament, kan du legge til funksjoner i økende grad:
- Teksturkart: lagre 2D-bilder og interpolere UV-koordinater på tvers av trekanter.
- Avansert skygge: Legg til spekulære høydepunkter (Blinn-Phong), omgivelsesokklusjon eller til og med skyggekart.
- Bruk et oktree eller BSP-tre for å raskt bestemme hvilke objekter som er synlige.
- Brukerinteraksjon: Håndtere tastatur og musinngang for å omkretse kameraet eller plukke objekter.
- Pipelinabstraksjon: Design et skyggesystem: definere hjørne- og fragmentfunksjoner som funksjonspekere, noe som muliggjør fleksibel gjengivelse uten rekompilasjon.
Resurser og videre lesing
For å utdype din forståelse, utforsk de klassiske tekstene og online opplæringene som inspirerte denne motoren:
- ⁇ Dybde forklaringer på strålesporing, rastisering og matematikk.
- LearnOpenGL ⁇ Moderne OpenGL-utdanninger; start med kapitlet \"Hallo Triangle\".
- Wikipedia: 3D-prosjekt ⁇ Den formelle matematikken bak perspektiv og ortografiske matriser.
Bygge en 3D-motor i C er en passage for grafikkprogrammerere. Det lærer deg ikke bare hvordan du skriver effektiv kode, men også hvordan du tenker i tre dimensjoner og komponere ytelseskritiske systemer. Start med en enkelt roterende kube, legg deretter til flere trekanter, teksturer og til slutt gi det til GPU. Reisen fra rå C til en fullt funksjonell motor avslører magien bak hver piksel på skjermen.