Benvenuto! Questa è la sesta parte della nostra serie Let's Build a 3D Graphics Engine che copre le basi dei sistemi di grafica 3D. Questa volta parleremo del colore e di come aggiungerlo alle nostre classi esistenti. Creeremo anche alcune funzioni utili per semplificare la gestione dell'illuminazione, che è la parte successiva e finale della nostra attività.
Aggiungere colori ai nostri oggetti non sarà un'impresa troppo grande, quindi le uniche due lezioni su cui ci concentreremo pesantemente sono la nostra point-class e la nostra classe di fotocamere. Come aggiornamento, ecco come sono:
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 tupla 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
Finora, il nostro motore teorico ha quasi tutte le basi in atto, tra cui:
Punto
e Vettore
classi (i mattoni del nostro motore).telecamera
classe (imposta la nostra finestra e elimina i punti all'esterno dello schermo).Ora aggiungiamo un po 'di colore!
Il nostro motore gestirà i colori memorizzando i loro valori al suo interno Punto
classe. Ciò consente a ciascun punto di avere il proprio colore individuale, rendendo i calcoli di illuminazione e ombreggiatura molto più semplici (per le persone, almeno - a volte è meno efficiente codificare un motore in questo modo). Quando capiamo l'illuminazione o l'ombreggiatura di una scena, possiamo facilmente fornire la funzione con una lista di punti, e quindi sfogliarli ognuno di essi, usando la loro distanza dalla luce per modificare il loro colore di conseguenza..
Uno dei metodi più comuni per archiviare il colore nella programmazione consiste nell'utilizzare i valori rosso, verde e blu per creare il colore desiderato effettivo (in genere viene chiamato miscelazione colore additiva). Memorizzando un valore di 0-255 in ciascuno di questi segmenti di colore, è possibile creare facilmente un'ampia varietà di colori. (Questo è il modo in cui la maggior parte delle API determina il colore, quindi per ragioni di compatibilità ha senso usare questo metodo).
Quindi, a seconda dell'API di grafica che si sta utilizzando, è possibile passare questi valori in forma decimale (255,0,0
), o in forma esadecimale (0xFF0000
o # FF0000
). Useremo il formato decimale nel nostro motore poiché è un po 'più facile da lavorare. Inoltre, se la tua API grafica utilizza valori esadecimali, probabilmente ha una funzione per la conversione da decimale a esadecimale, quindi questo non dovrebbe essere un problema.
Per iniziare l'implementazione del colore, aggiungeremo tre nuove variabili alla nostra classe Point: rosso
, blu
, e verde
. Non c'è ancora niente di troppo stravagante, ma ecco cosa è il nostro Punto
il nuovo schema della classe potrebbe avere il seguente aspetto:
Point Class Variables: num tuple [3]; // (x, y, z) num rosso, verde, blu; // può essere abbreviato in r, g, b se desiderato Operatori: Point AddVectorToPoint (Vector); Point SubtractVectorFromPoint (Vector); Vector SubtractPointFromPoint (Point); Null SetPointToPoint (Point); Funzioni: drawPoint; // disegna un punto nella sua posizione tupla
Questo è tutto ciò di cui abbiamo bisogno per memorizzare il colore del nostro punto. Ora dobbiamo solo regolare la funzione di disegno della nostra fotocamera in modo che utilizzi il colore specificato.
Questo cambierà drasticamente a seconda dell'API grafica che stai usando, ma dovrebbero avere tutti una funzione simile a questa:
object.setColor (rosso, verde, blu)
Se la tua API grafica utilizza valori esadecimali per il colore anziché i decimali, la tua funzione sarà simile a questa:
object.setColor (toHex (rosso, verde, blu))
Quest'ultimo bit usa a toHex ()
funzione (di nuovo, i nomi delle funzioni differiscono da API a API) per convertire un valore RGB in un valore esadecimale in modo da non dover.
Dopo aver apportato queste modifiche, ora dovresti essere in grado di avere punti colorati all'interno della scena. Per fare un ulteriore passo avanti, modificheremo ciascuna delle nostre classi di rasterizzazione in modo che la nostra intera forma possa essere colorata.
Per aggiungere questo alle nostre classi, dobbiamo semplicemente aggiungere la gestione del colore alle loro funzioni di costruzione. Questo potrebbe sembrare:
lineSegment :: constructor (startX, startY, endX, endY, red, green, blue) this.startX = startX; questo.startY = startY; this.endX = endX; this.endY = endY; this.red = red; this.green = verde; this.blue = blu;
Quindi, abbiamo solo bisogno di modificare la sua funzione di punti di ritorno in modo tale da impostare ogni punto della sua matrice in modo che abbia il colore specificato. La nuova funzione sarà simile a questa:
function returnPointsInSegment () // crea una lista per memorizzare tutti i punti del segmento di linea var pointArray = new Array (); // imposta le variabili di questa funzione in base ai punti iniziale e finale della classe var x0 = this.startX; var y0 = this.startY; var x1 = this.endX; var y1 = this.endY; // definisce le differenze vettoriali e le altre variabili richieste per Algorithm di Bresenham var dx = Math.abs (x1-x0); var dy = Math.abs (y1-y0); var sx = (x0 & x1)? 1: -1; // passo x var sy = (y0 & y1)? 1: -1; // step y var err = dx-dy; // ottiene il valore di errore iniziale // imposta il primo punto nell'array pointArray.push (new Point (x0, y0, this.red, this.green, this.blue)); // Ciclo di elaborazione principale while (! ((X0 == x1) && (y0 == y1))) var e2 = err * 2; // mantiene il valore dell'errore // usa il valore dell'errore per determinare se il punto deve essere arrotondato in alto o in basso se (e2 => -dy) err - = dy; x0 + = sx; if (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;
Ora, ogni punto all'interno del segmento di linea dovrebbe essere dello stesso colore che è stato passato nel segmento di linea. È possibile utilizzare questo metodo per impostare i colori anche nelle altre classi di rasterizzazione, e presto la scena si animerà di colori!
Mettiamo in azione le nostre nuove funzionalità creando un programma per mostrarle.
Usando il mixaggio additivo dei colori, possiamo facilmente creare oltre 16,7 milioni di colori diversi usando semplicemente il semplice (r, g, b
notazione. Creeremo un programma che sfrutta questo vasto numero di colori.
Usando la pressione dei tasti, consentiremo all'utente di controllare individualmente i valori rosso, verde e blu di un oggetto, consentendo loro di trasformarlo in qualsiasi colore che vorrebbero.
Le specifiche per il nostro programma sono le seguenti:
Tenendo tutto questo in mente, diamo un'occhiata a quale potrebbe essere il profilo di base del nostro programma:
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 classe 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; // memorizza i nostri colori in modo che possano essere manipolati var red, green, blue; // disegna l'oggetto iniziale e impostalo su una variabile while (key! = esc) if (key press = 'a') if (red> 0) red -; oggetto.rosso = rosso; // ridisegna oggetto if (tasto preme = 'q') if (rosso < 255) red ++; object.red = red; //redraw object if(key press = 's') if(green > 0) verde -; object.green = verde; // ridisegna oggetto if (tasto preme = 'w') if (verde < 255) green ++; object.green = green; //redraw object if(key press = 'd') if(blue > 0) blu -; oggetto.blu = blu; // ridisegna oggetto if (tasto preme = 'e') if (blu < 255) blue ++; object.blue = blue; //redraw object
Ora possiamo giocare con il nostro oggetto e trasformarlo in qualsiasi colore che desideri!
Controlla qui la demo - premi ripetutamente il tasto Q, W, E, UN, S, e D i tasti per cambiare il colore del quadrato.
Con il colore aggiunto nel nostro motore, abbiamo tutto ciò che ci serve per gestire finalmente l'illuminazione. Nel prossimo articolo, cercheremo di creare fonti di illuminazione e di creare alcune funzioni che consentano a tali fonti di influenzare i colori dei nostri punti. La profondità che l'illuminazione aggiunge a un motore è estremamente soddisfacente, quindi assicurati di verificarla!