Să construim un motor grafic 3D culori

Bine ati venit! Aceasta este a șasea parte din seria Let's Build a Graphics Engine care acoperă elementele de bază ale sistemelor grafice 3D. De data aceasta vom vorbi despre culoare și cum să o adăugăm în clasele noastre existente. Vom crea, de asemenea, câteva funcții utile pentru a ușura manipularea iluminatului, ceea ce va fi următoarea și ultima noastră parte.


Recapitulare

Adăugarea de culori la obiectele noastre nu va fi prea mare pentru o întreprindere, astfel încât singurele două clase pe care le vom concentra pe ele sunt clasa noastră de vârf și clasa noastră de camere. Ca o reîmprospătare, iată cum arată:

 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 poziția sa tuple 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

Până acum, motorul nostru teoretic are aproape toate elementele de bază, printre care:

  • Punct și Vector clase (componentele motorului nostru).
  • Funcțiile de transformare pentru punctele noastre.
  • A aparat foto (setează punctul de vedere al nostru și elimină punctele din afara ecranului).
  • Trei clase pentru rasterizare (segmente de linie, cercuri și poligoane).

Acum, să adăugăm o anumită culoare!


Culoare pentru toată lumea!

Motorul nostru se va ocupa de culori prin stocarea valorilor în interiorul acestuia Punct clasă. Acest lucru permite fiecărui punct să aibă propria culoare individuală, făcând calculele de iluminare și de umbrire mult mai simple (cel puțin pentru persoane - câteodată este mai puțin eficient să codificați un motor în acest fel). Atunci când găsim o iluminare a scenei sau o umbrire, putem oferi cu ușurință funcția cu o listă de puncte și apoi vom trece prin fiecare dintre ele, folosind distanța de la lumină pentru a-și modifica culoarea.

Una dintre cele mai comune metode de stocare a culorilor în programare este folosirea valorilor roșii, verzi și albastre pentru a crea culoarea dorită (acest lucru este denumit în mod obișnuit amestecarea culorii aditivilor). Stocând o valoare de 0-255 în fiecare dintre aceste segmente de culori, puteți crea cu ușurință o mare varietate de culori. (Acesta este modul în care cele mai multe API-uri determină culoarea, deci din motive de compatibilitate este logic să folosiți această metodă). 

Apoi, în funcție de grafica API pe care o utilizați, puteți trece aceste valori în formă zecimală (255,0,0) sau în formă hexazecimală (0xFF0000 sau # FF0000). Vom folosi formatul zecimal în motorul nostru, deoarece este ușor să lucrăm cu el. De asemenea, în cazul în care graficul API nu folosește valori hexazecimale, atunci probabil are o funcție pentru conversia de la zecimal la hexazecimal, deci nu ar trebui să fie o problemă.

Pentru a începe punerea în aplicare a culorilor noastre vom adăuga trei variabile noi la clasa Point: roșu, albastru, și verde.  Nu se întâmplă nimic extraordinar, dar aici este ceea ce este al nostru Punct noua schiță a clasei ar putea să arate ca:

 Clasa de puncte Variabile: număr [3]; // (x, y, z) numere roșii, verzi, albastre; // ar putea fi abreviat la r, g, b dacă se dorește Operatori: Point AddVectorToPoint (Vector); Punctul SubtractVectorFromPoint (Vector); Vector SubtractPointFromPoint (punct); Null SetPointToPoint (punct); Funcții: drawPoint; // trageți un punct în poziția sa tuple

Asta e tot ce avem nevoie pentru a stoca culoarea punctului nostru. Acum trebuie doar să reglat funcția de tragere a camerei noastre, astfel încât să utilizeze culoarea specificată.

Acest lucru se va schimba drastic în funcție de grafica API pe care o utilizați, dar toate ar trebui să aibă o funcție similară cu aceasta:

 object.setColor (roșu, verde, albastru)

În cazul în care graficul dvs. API se întâmplă să utilizeze valori hexazecimale pentru culoare în loc de zecimal, atunci funcția dvs. ar arăta similară cu aceasta:

 object.setColor (toHex (roșu, verde, albastru))

Ultimul bit utilizează a toHex () (din nou, numele funcțiilor vor diferi de la API la API) pentru a converti o valoare RGB într-o valoare hexazecimală, astfel încât să nu fie necesară.  

După ce ați făcut aceste modificări, ar trebui acum să aveți puncte colorate în scenă. Pentru a face un pas mai departe, vom ajusta fiecare dintre clasele noastre de rasterizare astfel încât întreaga noastră formă să poată fi colorată.

Pentru a adăuga acest lucru la clasele noastre, trebuie pur și simplu să adăugăm în manipularea culorilor funcțiile constructorului lor. Ar putea să arate:

 lineSegment :: constructor (startX, startY, endX, endY, roșu, verde, albastru) this.startX = startX; this.startY = startY; this.endX = endX; acest sfarsit = endY; this.red = roșu; this.green = verde; this.blue = albastru; 

Apoi, trebuie doar să modificăm funcția punctelor de întoarcere, astfel încât să se stabilească fiecare punct din matrice să aibă culoarea specificată. Noua funcție ar arăta astfel:

 funcția returnPointsInSegment () // a crea o listă pentru a stoca toate punctele segmentului de linie var pointArray = new Array (); // setați variabilele acestei funcții pe baza punctelor de început și de sfârșit ale clasei var x0 = this.startX; var y0 = acest început; var x1 = acest.endX; var y1 = acest lucru; // definește diferențele vectoriale și alte variabile necesare pentru algoritmul lui Bresenham var dx = Math.abs (x1-x0); var dy = Math.abs (y1-y0); var sx = (x0 & x1)? 1: -1; // pas x var sy = (y0 & y1)? 1: -1; // pas y var err = dx-dy; // obține valoarea inițială de eroare // setați primul punct în array pointArray.push (nou punct (x0, y0, this.red, this.green, this.blue)); // Bucla principală de procesare în timp ce (! ((X0 == x1) && (y0 == y1))) var e2 = err * 2; // păstrați valoarea erorii // utilizați valoarea erorii pentru a determina dacă punctul trebuie rotunjit în sus sau în jos dacă (e2 => -dy) err - = dy; x0 + = sx;  dacă (e2 < dx)  err += dx; y0 += sy;  //add the new point to the array pointArray.push(new Point(x0, y0,this.red,this.green,this.blue));  return pointArray; 

Acum, fiecare punct din segmentul de linie ar trebui să fie de aceeași culoare care a fost trecut în segmentul de linie. Puteți folosi această metodă pentru a seta culorile în celelalte clase de rasterizare, iar în curând scena dvs. va veni în viață cu culoarea!  

Să punem în aplicare noile noastre caracteristici, făcând un program care să le arate.


Redarea cu 16,7 milioane de culori

Folosind amestecarea culorilor adiționale, putem crea cu ușurință peste 16,7 milioane de culori diferite folosind doar simpla (r, g, b) notație. Vom crea un program care profită de acest număr vast de culori.

Folosind apăsările de taste, vom permite utilizatorului să controleze valorile roșii, verzi și albastre ale unui obiect în parte, permițându-le să facă orice culoare pe care o vor.

Specificațiile pentru programul nostru sunt următoarele:

  • Desenați un obiect pe ecran.
  • Dacă utilizatorul apasă A apoi scade valoarea roșie a obiectului; dacă apasă Q apoi ridicați-o.
  • Dacă utilizatorul apasă S apoi scade valoarea verde a obiectului; dacă apasă W apoi ridicați-o.
  • Dacă utilizatorul apasă D apoi scade valoarea albastră a obiectului; dacă apasă E apoi ridicați-o.
  • Redeschideți obiectul după ce culoarea sa a fost actualizată.
  • Asigurați-vă că limitați valorile culorilor, împiedicându-le să scadă sub 0 sau să crească peste 255.

Având în vedere toate acestea, să aruncăm o privire la ce ar putea să arate o schemă de bază a programului nostru:

 main // setup pentru grafica API preferată aici // configurare pentru introducerea tastaturii (poate să nu fie necesară) aici var camera = new Camera (); // creați o instanță a clasei camerei // setați camera de vizualizare a camerei camera.minX = 0; camera.maxX = ecranWidth; camera.minY = 0; camera.maxY = screenHeight; camera.minZ = 0; camera.maxZ = 100; // stoca culorile noastre astfel încât să poată fi manipulate var roșu, verde, albastru; // trageți obiectul inițial și setați-l la o variabilă în timp ce (key! = esc) if (tasta apăsați = 'a') if (roșu> 0) roșu -; object.red = roșu; // redraw object dacă (apăsați tasta = 'q') if (roșu < 255)  red ++; object.red = red; //redraw object   if(key press = 's')  if(green > 0) verde -; object.green = verde; // redraw object dacă (apăsați tasta = 'w') if (verde < 255)  green ++; object.green = green; //redraw object   if(key press = 'd')  if(blue > 0) albastru -; object.blue = albastru; // redraw object dacă (apăsați tasta = 'e') if (albastru < 255)  blue ++; object.blue = blue; //redraw object    

Acum putem să jucăm cu obiectul nostru și să facem orice culoare dorită!

Consultați demo-ul aici - apăsați repetat tasta Q, W, E, A, S, și D tastele pentru a schimba culoarea pătratului.


Concluzie

Cu culoarea adăugată în motorul nostru, avem tot ceea ce avem nevoie pentru a rezolva în cele din urmă câteva lumini. În următorul articol, vom analiza crearea surselor de iluminare și crearea unor funcții pentru a permite acestor surse să afecteze culorile punctelor noastre. Adâncimea pe care o aduce iluminarea unui motor este extrem de satisfăcătoare, deci asigurați-vă că o verificați!