Costruiamo un motore grafico 3D rasterizzazione di triangoli e quad

Benvenuti alla quinta parte della nostra serie Let's Build a 3D Graphics Engine! Questa volta, costruiremo due nuove classi per rasterizzare: una per i triangoli e una per i quadrilateri di base. Quindi, prenderemo pezzi da quelle due classi e metteremo una classe poligonale finale, onnipotente.

Mancia: Questa è una parte di una serie, quindi se vuoi ottenere il massimo da esso assicurati di leggere gli altri tutorial che portano a questo.


Ricapitolare

Abbiamo costruito un bel po 'nel nostro motore finora! Ecco cosa abbiamo:

  • Classi Point e Vector (i mattoni del nostro motore).
  • Funzioni di trasformazione per i nostri punti.
  • Una classe Camera (imposta la nostra finestra e elimina i punti al di fuori dello schermo).
  • Due classi per rasterizzare (segmenti di linea e cerchi).

Ecco un rapido riferimento per tutte le classi che abbiamo creato:

 Point Class Variables: num tuple [3]; // (x, y, z) Operatori: Punto AddVectorToPoint (Vettore); Point SubtractVectorFromPoint (Vector); Vector SubtractPointFromPoint (Point); Null SetPointToPoint (Point); Funzioni: drawPoint; // disegna un punto nella sua posizione tuple Vector Class Variables: num tuple [3]; // (x, y, z) Operatori: Vector AddVectorToVector (Vector); Vector SubtractVectorFromVector (Vector); Vector RotateXY (gradi); Vector RotateYZ (gradi); Vector RotateXZ (gradi); Scala vettoriale (s0, s1, s2); // riceve una tupla a 3 scale, restituisce il vettore scalato Camera Class Vars: int minX, maxX; int minY, maxY; int minZ, maxZ; array objectsInWorld; // una matrice di tutti gli oggetti esistenti Funzioni: null drawScene (); // disegna tutti gli oggetti necessari sullo schermo LineSegment Class Variables: int startX, startY; // il punto di partenza del nostro segmento di linea int endX, endY; // il punto finale del nostro segmento di linea Funzione: array returnPointsInSegment; // tutti i punti che si trovano su questo segmento di linea

Faremo molto affidamento sul Segmento classe per creare il nostro Triangolo e quadrilatero classi, quindi assicurati di familiarizzarti con esso prima di proseguire.


Rasterizing Triangles

Mettendo insieme a Triangolo la classe per il motore è abbastanza semplice, specialmente dal Segmento la classe è dove tutta la nostra rasterizzazione avrà effettivamente luogo. Questa classe consentirà di impostare tre punti e traccerà un segmento di linea tra loro per creare il triangolo completato.  

Un profilo di base della classe potrebbe essere simile a questo:

 Triangle Class Variables: // coordina per i tre punti dei nostri triangoli int Point1X, Point1Y; int Point2X, Point2Y; int Point3X, Point3Y; Funzione: array returnPointsInTriangle; // tutti i punti all'interno del perimetro del triangolo

Per ragioni di standard, assumeremo che i tre punti dichiarati all'interno del nostro triangolo siano in uno schema orario.  

Usando il nostro Segmento classe, quindi, possiamo impostare il nostro returnPointsInTriangle () funzione come questa:

 function returnPointsInTriangle () array PointsToReturn; // crea una matrice temporanea per contenere i punti del triangolo // Crea tre segmenti di linea e memorizza i loro punti nell'array PointsToReturn.push (nuovo LineSegment (this.Point1X, this.Point1Y, this.Point2X, this.Point2Y)); PointsToReturn.push (nuovo LineSegment (this.Point2X, this.Point2Y, this.Point3X, this.Point3Y)); PointsToReturn.push (nuovo LineSegment (this.Point3X, this.Point3Y, this.Point1X, this.Point1Y)); ritorno (PointsToReturn); 

Non male, giusto? Dal momento che abbiamo già un sacco di lavoro nel nostro Segmento classe, dobbiamo solo continuare ad unirli insieme per creare forme più complesse. Ciò semplifica la creazione di poligoni sempre più complicati sullo schermo, semplicemente aggiungendo altro LineSegments (e memorizzando più punti all'interno della classe stessa).

Quindi, diamo un'occhiata a come possiamo aggiungere più punti a questo sistema creando una classe quadrata.


Ottenere Squared Away

Mettere insieme una classe per gestire i quadrilateri comporta solo l'aggiunta di alcune cose extra al nostro Triangolo classe. Con un'altra serie di punti, la nostra classe quadrilatera sarebbe simile a questa:

 Classe quad Variabili: int Point1X, Point1Y; // coordinate per i quattro punti del nostro quadrilatero int Point2X, Punto2Y; int Point3X, Point3Y; int Point4X, Point4Y; Funzione: array returnPointsInQuad; // restituisce tutti i punti all'interno del quadrilatero

Quindi, aggiungiamo semplicemente il segmento aggiuntivo alla linea returnPointsInQuad funzione, in questo modo:

 function returnPointsInQuad () array PointsToReturn; // crea un array temporaneo per contenere i punti del quad // Crea quattro segmenti di linea e memorizza i punti nell'array PointsToReturn.push (nuovo LineSegment (this.Point1X, this.Point1Y, this.Point2X, this.Point2Y)); PointsToReturn.push (nuovo LineSegment (this.Point2X, this.Point2Y, this.Point3X, this.Point3Y)); PointsToReturn.push (nuovo LineSegment (this.Point3X, this.Point3Y, this.Point4X, this.Point4Y)); PointsToReturn.push (nuovo LineSegment (this.Point4X, this.Point4Y, this.Point1X, this.Point1Y)); ritorno (PointsToReturn); 

Mentre la costruzione di nuove classi come questa è abbastanza semplice, c'è un modo molto più semplice per incapsulare tutti i nostri poligoni in un'unica classe. Usando la magia di loop e matrici, possiamo mettere insieme una classe poligonale che potrebbe rendere qualsiasi forma di qualsiasi dimensione che si possa desiderare!


Dove sono tutti i Polys-Gon?

Per creare una classe poligonale in continua espansione, dobbiamo fare due cose. Il primo è spostare tutti i nostri punti in un array, il che ci darebbe un profilo di classe simile a qualcosa di simile a questo:

 Poligono Class Variabili: punti dell'array; // contiene tutti i punti del poligono in un array Funzione: array returnPointsInPolygon; // un array che contiene tutti i punti del poligono

Il secondo è utilizzare un ciclo per consentire ad un numero senza nome di segmenti di linea di essere attraversati nel nostro returnPointsInPolygon () funzione, che potrebbe assomigliare a questo:

 function returnPointsInPolygon array PointsToReturn; // una matrice temporanea per mantenere il ciclo // dei punti del poligono attraverso tutti i punti del poligono, spostando una coppia di coordinate alla volta (di un passo di due) per (int x = 0; x < this.Points.length; x+=2)  if(this is not the last point)  //create a line segment between this point and the next one in the array PointsToReturn.push(new LineSegment(this.Points[x], this.Points[x+1], this.Points[x+2], this.Points[x+3]));  else if(this is the last point)  //create a line segment between this point and the first point in the array PointsToReturn.push(new LineSegment(this.Points[x-2], this.Points[x-1], this.Points[0], this.Points[1]));   //return the array of points return PointsToReturn; 

Con questa classe aggiunta al nostro motore, ora possiamo creare qualsiasi cosa, da un triangolo ad un abominio a 39 lati con la stessa linea di codice.


Creatore di poligoni

Per giocare con la nostra nuova classe poligonale, facciamo un programma che mostri l'estensione della sua portata. Il nostro programma consentirà all'utente di aggiungere o rimuovere lati dal poligono visualizzato usando le pressioni dei tasti. Ovviamente, dovremo impostare dei limiti per il numero di lati che il nostro poligono può avere, poiché avere meno di tre lati non lo renderà più un poligono. Non dobbiamo davvero tenere d'occhio i limiti superiori del nostro poligono perché dovrebbero scalare bene. Tuttavia, stiamo limitando i poligoni ad avere dieci lati al massimo, poiché fisseremo i nuovi punti all'interno del codice.

Le nostre specifiche del programma possono essere suddivise in queste parti più piccole:

  • Disegna inizialmente un poligono sullo schermo.
  • Quando viene premuto il tasto 'a', ridurre il numero di lati del poligono di 1.
  • Premendo il tasto 's', aumenta il numero di lati del poligono di 1.
  • Evitare che il numero di lati del poligono scenda al di sotto del 3.
  • Impedisci al numero di lati del poligono di aumentare al di sopra di 10.

Diamo un'occhiata a come potrebbe essere il nostro codice:

 main // setup per la tua API grafica preferita qui // setup per l'input da tastiera (potrebbe non essere necessario) qui var camera = new Camera (); // crea un'istanza della nostra telecamera classe camera.objectsInWorld []; // inizializza l'array di oggetti della telecamera // imposta lo spazio di visualizzazione della telecamera camera.minX = 0; camera.maxX = screenWidth; camera.minY = 0; camera.maxY = screenHeight; camera.minZ = 0; camera.maxZ = 100; // crea una serie di punti per ogni dimensione di poligono var threeSides = new Array (100,100,100,50,50,50); var fourSides = new Array (punta qui); var fiveSides = new Array (punta qui); var sixSides = new Array (punta qui); var sevenSides = new Array (punta qui); var eightSides = new Array (punta qui); var nineSides = new Array (punta qui); var tenSides = new Array (punta qui); // memorizza tutti gli array in un altro array per un accesso più semplice var sidesArray = new Array (threeSides, fourSides, fiveSides, sixSides, sevenSides, eightSides, nineSides, tenSides); // tiene traccia di quanti punti il ​​poligono ha attualmente var polygonPoints = 3; // crea il poligono iniziale da visualizzare var polygon = new Polygon (sidesArray [0] [0], sidesArray [0] [1], sidesArray [0] [2], sidesArray [0] [3], sidesArray [0 ] [4], sidesArray [0] [5],); // disegna il poligono iniziale sullo schermo camera.drawScene (); // mentre l'utente non ha premuto la chiave di escape while (key! = esc) if (tasto premuto == 'a') // se il poligono non rischia di scendere sotto 3 if (polygonPoints! = 3) // riduci il numero di punti polygonPoints--; // cambia il poligono per avere il numero corretto di punti // ridisegna la scena camera.drawScene ();  else if (tasto premuto == 's') // se il poligono non rischia di andare oltre 10 if (polygonPoints! = 10) // aumentare il numero di punti polygonPoints ++; // cambia il poligono per avere il numero corretto di punti // ridisegna la scena camera.drawScene (); 

Il nostro piccolo programma dovrebbe permettervi di regolare un poligono sullo schermo ora! Guarda la demo. Se vuoi rinforzare questo programma un po ', potresti provare a mettere la sezione di alterazione del poligono in una qualche forma di un algoritmo per renderti più semplice il ridimensionamento. Non sono sicuro che esista già, ma se lo facesse, potresti facilmente avere un poligono a variazione continua sulle tue mani!


Conclusione

Abbiamo una quantità piuttosto estesa di rasterizzazione integrata nel nostro motore ora, che ci consente di creare quasi tutte le forme che potremmo avere bisogno (anche se alcune solo attraverso la combinazione). La prossima volta ci allontaneremo dal disegnare forme e parleremo più delle loro proprietà. Se sei interessato a portare un po 'di colore sul tuo schermo, assicurati di controllare la parte successiva!