Stichtingen: vertegenwoordigen van driedimensionale ruimte

Voordat een enkele pixel wordt getekend, moet een 3D-motor een wiskundige taal hebben om objecten in de ruimte te beschrijven. In de kern bevinden zich vectoren (driecomponententupels die positie, richting of kleur vertegenwoordigen) en matrices (4×4 arrays die transformaties coderen). In C worden deze meestal gedefinieerd als eenvoudige structuren:

  • voor punten en vectoren.
  • voor transformatiematrices die in kolom-major order zijn opgeslagen.

Elk object bestaat uit driehoeken. Een driehoek wordt gedefinieerd door drie hoekpunten, elk met een positie, en optioneel een normale en kleur. Deze als aaneengesloten arrays (bijv. ] voor een kubus opslaan) verbetert de cache-efficiëntie een kritische overweging wanneer u later duizenden gezichten per frame verwerkt.

De Graphics Pipeline: Van Vertices naar Pixels

Een grafische pijplijn is een reeks fasen die 3D-scènegegevens transformeert in een 2D-image. In een basissoftware-rendered engine, implementeert u handmatig elke fase. De belangrijkste fasen zijn:

  1. Vertexverwerking . . . Het toepassen van model, weergave en projectie transformeert.
  2. Rasterisatie ..omgezette driehoeken om te zetten in fragmenten (potentiële pixels).
  3. Shading ..berekenen van de kleur van elk fragment op basis van verlichting en materiaal.
  4. Uitvoeren van samenvoegen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

Hardware-versnelde motoren (met behulp van OpenGL, Vulkan, of DirectX) voeren de meeste van deze stappen op de GPU, maar het begrijpen van de software pad geeft u diep inzicht in hoe de GPU werkt onder de kap.

Vertexverwerking en transformatie

Elke vertex begint in modelruimte (lokale coördinaten ten opzichte van het object). Om het in de wereld te positioneren, past u een modelmatrix toe die vertaling, rotatie en schaalvergroting codeert. Dan transformeert de viewmatrix wereldcoördinaten in camerarelatieve coördinaten (view space). Ten slotte wordt de ]projectionmatrix[ kaarten de ruimte bekijken naar een genormaliseerd coördinatensysteem ([] clipruimte[) waar perspective deling en viewport mapping schermcoördinaten produceren.

In C zien transformatiefuncties eruit als:

  • .. rotatie rond de X-as.

De vertex shader equivalent in software itereert over alle hoekpunten, vermenigvuldigt elk door de gecombineerde matrix, en slaat het resultaat op in een getransformeerde vertex buffer.

Projectie: Perspectief versus Orthografisch

Projectie bepaalt hoe diepte op het scherm wordt weergegeven. Perspectieve projectie maakt verre objecten kleiner, waardoor realisme ontstaat. De matrix wordt gebouwd vanuit het gezichtsveld, de aspectverhouding, nabij en ver knippervlakken. De klassieke formule omvat het delen van de x en y componenten door z (na de matrix vermenigvuldiging). In C, je berekent de projectiematrix eenmaal en hergebruikt het elk frame:

  • [[FLT:]]

Orthografisch projectie behoudt parallelle lijnen en is eenvoudiger, gebruikt voor UI- of CAD-tools. Het gaat niet om perspectiefverdeling.

Rasterisatie: Driehoeken vullen

Na projectie heb je 2D schermcoördinaten (zoals pixelposities) en een dieptewaarde (z). Rasterisatie breekt elke driehoek in fragmenten die integer pixelcoördinaten omvatten. Het klassieke algoritme is scanlijnconversie:

  1. Sorteer de driehoeken drie hoekpunten op y-coördinaat.
  2. Loop naar beneden de linker- en rechterkanten, het berekenen van x grenzen voor elke scanlijn.
  3. Voor elke pixel in de horizontale spanwijdte, berekent u de fragmentdiepte door te interpoleren over de driehoek.
  4. Voer een dieptetest uit: vergelijk het fragment met de waarde die al in de dieptebuffer zit. Als deze dichterbij komt, verwerk de dieptebuffer en bereken de kleur van het fragment.

Het efficiënt uitvoeren van het edge-walking algoritme in C vereist strakke lussen en zorgvuldig gebruik van vaste punt rekenen om drijvende-punt bovenzijde te voorkomen. Veel hobbyisten motoren beginnen met een brute-force benadering (controleer elke pixel in de aangrenzende doos) en later optimaliseren met randvergelijkingen.

Schaduwen en verlichting

Een eenvoudige platte motor gebruikt een enkele kleur per driehoek, berekend vanuit het gezicht normaal en een enkele lichtbron. Het Lambertiaan model geeft diffuse intensiteit: . In C, je het normale als het kruisproduct van twee driehoeksranden (na het transformeren van hoekpunten naar wereldruimte), vermenigvuldigt dan de lichtintensiteit door de driehoek basiskleur.

Om diepte toe te voegen, implementeer Gouraud shading (vertex normals, geïnterpoleerd over fragmenten) of Phong shading (perpixel normals). Deze laatste vereist interpolerende wereldposities en normalen, dan computerverlichting per fragment duurder maar dramatisch betere resultaten.

Bouwen van de motor in C: praktische stappen

Laat ons een minimale software 3D motor vanaf nul bouwen. Het doel is om een roterende kubus met ambient en diffuse verlichting te maken op een 640×480 pixel venster.

Stap 1

Definieer een geheugenbuffer voor het frame en de diepte: en . Maak de framebuffer zwart en de dieptebuffer tot 1,0 (vervlak) per frame.

Stap 2

Voor een kubus hebben zes gezichten (12 driehoeken) 12×3 = 36 hoekpunten nodig. Bewaar ze in een reeks structuren met positie (3 floats) en kleur (3 floats voor RGB). Pre-definieer de 8 unieke kubus hoeken en zes gezichtskleuren. Gebruik een index buffer om te voorkomen dat dupliceren hoekpunten.

Stap 3 . Transformatie pijpleiding

Elk frame, berekent de modelmatrix vanuit een hoek (bv. roteY(time)), de weergavematrix vanuit een vaste camera die naar de oorsprong kijkt, en de projectiematrix. Combineer ze in een enkele MVP-matrix: ]. Verander elke vertex door te vermenigvuldigen met ]. Voer vervolgens perspectiefverdeling (verdeel x,y,z door w) uit om genormaliseerde apparaatcoördinaten te krijgen (−1 tot +1). Kaart naar schermcoördinaten: , (y omgekeerd). Houd de NDC z voor dieptetest.

Stap 4

Voor elke driehoek, gebruik de scanline methode. Tijdens het lopen pixels, interpoleer de diepte en ook interpoleer een per-vertex attribuut zoals de wereld-ruimte positie (voor Phong) of de vertex normaal (voor Gouraud). Pas de Lambertiaanse diffuse vergelijking. Schrijf de laatste kleur op de framebuffer als de diepte test passeert.

Stap 5

Gebruik SDL (Simple DirectMedia Layer) of een vergelijkbare bibliotheek om een venster te creëren en de framebuffer te blikken. Bijvoorbeeld met SDL2: , , in , dan en ] elk frame.

Dit softwarepad draait op de CPU, dus de prestaties zijn beperkt tot een paar duizend driehoeken per frame. Eenmaal comfortabel, kunt u de rasterization laag te vervangen door OpenGL, het afladen van het zware werk naar de GPU terwijl het houden van uw transformatie en scène logica in C.

Uw motor optimaliseren

Een basismotor is traag. Verschillende optimalisaties zijn essentieel voor real-time prestaties:

  • Achterkant afkicken: Skip driehoeken waarvan de normalen zich van de camera afwenden. Bereken het getekende gebied van de geprojecteerde driehoek (in 2D) of controleer het puntproduct van het gezicht normaal met de kijkrichting.
  • Frustumafslachting: Test de omlijnde doos van een object tegen de zes vlakken van het zichtfrustum. Als het geheel naar buiten is, sla het gehele object over.
  • Vaste punt rekenen: Vervang floating-point operaties door integer wiskunde in strakke rasterisatie loops. Veel motorbouwers gebruiken 16.16 vaste-punt voor scanline interpolatie.
  • Pre-transform cache: Vermijd het transformeren van dezelfde vertex meerdere keren door het transformeren van alle hoekpunten van een mesh eenmaal, dan met behulp van indices.
  • Geblokte framebuffer schrijft: Schrijf pixels in geheugen-volgorde (links naar rechts, boven naar beneden) om cache-hits te maximaliseren.

Deze technieken kunnen een software renderer van 3

Uitbreiding van de motor

Zodra een solide fundering bestaat, kunt u functies incrementele toevoegen:

  • Textuur mapping: 2D-beelden opslaan en UV-coördinaten over driehoeken inpoleren. Bilineair filteren voor gladde resultaten.
  • Geavanceerde schaduw: Voeg spectaculaire hoogtepunten (Blinn-Phong), omgevingsocclusie, of zelfs schaduwkaarten toe.
  • Scene management: Gebruik een octree of BSP boom om snel te bepalen welke objecten zichtbaar zijn.
  • Gebruikersinteractie: Gebruik het toetsenbord en muisinvoer om de camera te draaien of objecten te kiezen.
  • Pipeline abstraction: Ontwerp een shadersysteem: definieer vertex en fragment functies als functiepointers, waardoor flexibele rendering zonder recompilatie mogelijk is.

Middelen en verdere lezing

Om uw begrip te verdiepen, verkent u de klassieke teksten en online tutorials die deze motor inspireerden:

  • Scratchapixel . . . Een diepgaande uitleg van ray traceren, rasteriseren en wiskunde.
  • LeerOpenGL . . Moderne OpenGL tutorials; begin met het hoofdstuk .Hallo Driehoek.
  • Wikipedia: 3D Projection . . De formele wiskunde achter perspectief en orthografische matrices.

Het bouwen van een 3D-motor in C is een rite van passage voor grafische programmeurs. Het leert je niet alleen hoe je efficiënte code te schrijven, maar ook hoe te denken in drie dimensies en samenstelling van prestatie-kritische systemen. Begin met een enkele roterende kubus, voeg dan meer driehoeken, texturen, en uiteindelijk overhandigen aan de GPU. De reis van rauwe C naar een volledig functionele motor onthult de magie achter elke pixel op het scherm.