Să construim un motor grafic 3D Triunghiuri și quaduri rasterizante

Bine ați venit în cea de-a cincea parte a seriei Engineer Graphics Engine! De data aceasta, vom construi două clase noi pentru rasterizare: una pentru triunghiuri și una pentru quadrilaterals de bază. Apoi, vom lua bucăți din aceste două clase și vom pune împreună o clasă finală, atotputernică, de poligoane.

Bacsis: Aceasta este o parte dintr-o serie, deci dacă doriți să obțineți cât mai mult din ele, asigurați-vă că ați citit celelalte tutoriale care au condus la aceasta.


Recapitulare

Am construit destul de mult în motorul nostru până acum! Iata ce avem:

  • Clasele Point și Vector (elementele de bază ale motorului nostru).
  • Funcțiile de transformare pentru punctele noastre.
  • O clasă de camere (setează punctul de vedere al camerei noastre și elimină punctele din afara ecranului).
  • Două clase pentru rasterizare (segmente de linie și cercuri).

Iată o referință rapidă pentru toate clasele pe care le-am construit:

 Clasa de puncte Variabile: număr [3]; // (x, y, z) Operatori: Point AddVectorToPoint (Vector); Punctul SubtractVectorFromPoint (Vector); Vector SubtractPointFromPoint (punct); Null SetPointToPoint (punct); Funcții: drawPoint; // desenați un punct la tupla sa de poziție Clasa Vector Variables: num tuple [3]; // (x, y, z) Operatori: Vector AddVectorToVector (Vector); Vector SubtractVectorFromVector (Vector); Vector RotateXY (grade); Vector RotateYZ (grade); Vector RotateXZ (grade); Vector Scară (s0, s1, s2); // primește o scalare 3-tuple, returnează vectorul scalat Clasa camerei Vars: int minX, maxX; int minY, maxY; int minZ, maxZ; obiecte arrayInWorld; // o matrice a tuturor obiectelor existente Funcții: null drawScene (); // atrage toate obiectele necesare pe ecran ClassSegment Class Variables: int startX, startY; // punctul de plecare al segmentului nostru de linie int endX, endY; // punctul de sfarsit al segmentului nostru de linie Functie: matrice returnPointsInSegment; // toate punctele situate pe acest segment de linie

Vom continua să ne bazăm foarte mult pe Segment de linie clasa pentru a ne crea Triunghi și Quad clase, așa că asigurați-vă că vă refamiliarizați-vă înainte de a vă deplasa.


Rasterizarea triunghiurilor

Reunirea a Triunghi clasa pentru motor este destul de simplă, mai ales că Segment de linie clasa este locul în care toată rasterizarea noastră va avea loc în realitate. Această clasă va permite să se stabilească trei puncte și va desena un segment între ele pentru a realiza triunghiul complet.  

O schiță de bază a clasei ar putea arăta astfel:

 Clasa triunghi Variabile: // coordonează pentru cele trei puncte ale triunghiurilor noastre int Point1X, Point1Y; int Point2X, Point2Y; int Point3X, Point3Y; Funcție: matrice returnPointsInTriangle; // toate punctele din perimetrul triunghiului

Din motive de standarde, vom presupune că cele trei puncte declarate în triunghiul nostru sunt într-un model în sens orar.  

Folosirea noastră Segment de linie clasa, atunci putem să ne înființăm returnPointsInTriangle () funcționează astfel:

 funcția returnPointsInTriangle () array PointsToReturn; // creați o matrice temporară pentru a ține punctele triunghiului // Creați trei segmente de linie și păstrați punctele lor în tabloul PointsToReturn.push (noul LineSegment (this.Point1X, this.Point1Y, this.Point2X, this.Point2Y)); PointsToReturn.push (noul LineSegment (this.Point2X, this.Point2Y, this.Point3X, this.Point3Y)); PointsToReturn.push (noul LineSegment (this.Point3X, this.Point3Y, this.Point1X, this.Point1Y)); întoarce (PointsToReturn); 

Nu prea rău, nu-i așa? Deoarece avem deja multe lucruri făcute în cadrul nostru Segment de linie trebuie să continuăm să le strângem împreună pentru a crea forme mai complexe. Acest lucru facilitează crearea de poligoane din ce în ce mai complicate pe ecran, pur și simplu adăugând mai multe LineSegments (și stocarea mai multor puncte în cadrul clasei).

Apoi, să aruncăm o privire asupra modului în care putem adăuga mai multe puncte la acest sistem prin crearea unei clase pătrate.


Noțiuni de bază Squared Away

Punerea împreună a unei clase pentru a trata quadrilaterals implică doar adăugarea de câteva lucruri suplimentare pentru a noastră Triunghi clasă. Cu un alt set de puncte, clasa noastră patrulaterală ar arăta astfel:

 Clasa Quad Variabile: int Point1X, Point1Y; // coordonează pentru cele patru puncte ale lui quadrilateral int Point2X, Point2Y; int Point3X, Point3Y; int Point4X, Point4Y; Funcție: matrice returnPointsInQuad; // retur toate punctele din cadrul patrulaterale

Apoi, adăugăm doar segmentul de linie suplimentară la returnPointsInQuad funcția, cum ar fi:

 funcția returnPointsInQuad () array PointsToReturn; // creați o matrice temporară pentru a menține punctele quad // Creați patru segmente de linie și păstrați punctele lor în matricele PointsToReturn.push (noul LineSegment (this.Point1X, this.Point1Y, this.Point2X, this.Point2Y)); PointsToReturn.push (noul LineSegment (this.Point2X, this.Point2Y, this.Point3X, this.Point3Y)); PointsToReturn.push (noul LineSegment (this.Point3X, this.Point3Y, this.Point4X, this.Point4Y)); PointsToReturn.push (noul LineSegment (acest.Point4X, this.Point4Y, this.Point1X, this.Point1Y)); întoarce (PointsToReturn); 

În timp ce construim clase noi precum acest lucru este destul de direct, există o modalitate mult mai ușoară de încapsulare a tuturor poligoanelor noastre într-o singură clasă. Prin folosirea magiei buclelor și a matricilor, putem pune împreună o clasă de poligon care ar putea face aproape orice formă de dimensiune pe care ați putea dori!


Unde au toate Polys-Gon?

Pentru a crea o clasă poligon extinctivă, trebuie să facem două lucruri. Primul este să ne mișcăm toate punctele într-o matrice, ceea ce ne va da o schiță de clasă asemănătoare cu ceva de genul:

 Clasa poligonală Variabile: matrice Puncte; // deține toate punctele poligonului într-o matrice Funcție: matrice returnPointsInPolygon; // o matrice care deține toate punctele poligonului

Al doilea este de a utiliza o buclă pentru a permite unui număr nesemnat de segmente de linie să fie traversate în noi returnPointsInPolygon () care ar putea arata cam asa:

 funcția returnPointsInPolygon array PointsToReturn; // o matrice temporară pentru a ține punctele poligonului // prin toate punctele din poligon, deplasând o pereche de coordonate la un moment dat (cu un pas de două) pentru (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; 

Cu această clasă adăugată la motorul nostru, putem crea acum orice, de la un triunghi la o abominație de 39 de laturi, cu aceeași linie de cod.


Creator de poligoane

Pentru a juca cu noua noastră clasă de poligoane, să realizăm un program care să indice gradul de acoperire al acesteia. Programul nostru va permite utilizatorului să adauge sau să îndepărteze laturile de la poligonul afișat prin apăsarea tastelor. Desigur, va trebui să stabilim limite pentru numărul de laturi pe care poligonul nostru le poate avea, din moment ce mai puțin de trei laturi nu-l vor mai face un poligon. Nu trebuie să ținem cu vederea limitele superioare ale poligonului nostru, deoarece ar trebui să scadă frumos. Cu toate acestea, vom limita poligoanele la maxim zece laturi, deoarece vom stabili noile puncte din cod.

Specificațiile programului nostru pot fi împărțite în următoarele părți mai mici:

  • Desenați inițial un poligon pe ecran.
  • Când tasta "a" este apăsată, reduceți numărul laturilor de pe poligon cu 1.
  • Când tasta 's' este apăsată, creșteți numărul laturilor de pe poligon cu 1.
  • Împiedicați ca numărul laturilor poligonului să scadă sub 3.
  • Împiedicați creșterea numărului de laturi a poligonului de peste 10.

Să ne uităm la ce ar putea să arate codul nostru:

 main // setup pentru grafica API preferată aici // configurare pentru introducerea tastaturii (poate să nu fie necesară) aici var camera = new Camera (); // a crea o instanță a camerei noastre camera.objectsInWorld []; // inițializați matricea de obiecte a aparatului foto // configurați camera camerei de vizualizare a camerei.minX = 0; camera.maxX = ecranWidth; camera.minY = 0; camera.maxY = screenHeight; camera.minZ = 0; camera.maxZ = 100; // a crea o serie de puncte pentru fiecare dimensiune poligon var threeSides = array nou (100,100,100,50,50,50); var fourSides = array nou (puncte aici); var fiveSides = array nou (punctele de aici); var sixSides = array nou (punctele de aici); var sevenSides = array nou (puncte aici); var eightSides = array nou (puncte aici); var nineSides = array nou (punctele de aici); var tenSides = Array nou (puncte aici); // stocați toate matricele într-o altă matrice pentru a avea acces mai ușor var sidesArray = Array nou (trei părți, patru părți, cinci părți, șase pagini, șapte pagini, opt părți, nouă pagini, zece părți); // urmăriți câte puncte are în prezent poligonul var polgonPoints = 3; // creati poligonul initial care va fi afisat var polygon = new Polygon (sidesArray [0] [0], sidesArray [0] [1], sidesArray [0] [2], sidesArray [0] [3] ] [4], lateralArray [0] [5],); // trageți poligonul inițial pe camera.drawScene (); // în timp ce utilizatorul nu a apăsat tasta de evacuare în timp ce (key! = esc) if (tasta apăsată == 'a') // dacă poligonul nu riscă să scadă sub 3 dacă (polygonPoints! = 3) // reduce numărul de puncte polygonPoints--; // modificați poligonul pentru a avea numărul corect de puncte // redrawați scena camera.drawScene ();  altceva (dacă tasta este apăsată == 's') // dacă poligonul nu riscă să depășească 10 dacă polygonPoints! = 10) // crește numărul de puncte polygonPoints ++; // modificați poligonul pentru a avea numărul corect de puncte // redrawați scena camera.drawScene (); 

Programul nostru mic vă permite să ajustați un poligon pe ecran acum! Check out demo-ul. Dacă doriți să transformați acest program într-un pic, puteți încerca să puneți secțiunea de modificare a poligonului într-o formă de algoritm pentru a vă face mai ușoară scalarea. Nu sunt sigur dacă există deja, dar dacă se întâmplă, puteți avea cu ușurință un poligon infinit de scalare pe mâini!


Concluzie

Avem o cantitate destul de mare de rasterizare încorporată în motorul nostru acum, permițându-ne să creăm aproape orice formă pe care am putea avea nevoie (deși unii numai prin combinație). Data viitoare vom fi departe de a desena forme și de a vorbi mai multe despre proprietățile lor. Dacă sunteți interesat să aduceți o anumită culoare pe ecran, asigurați-vă că ați verificat următoarea parte!