Costruiamo un motore grafico 3D illuminazione dinamica

Ciao! Questo è l'ultimo articolo della nostra serie sulle basi dei sistemi di grafica 3D, e questa volta guarderemo all'illuminazione dinamica! Prima di essere troppo eccitato o troppo preoccupato per quanto difficile possa essere un compito come l'illuminazione dinamica, rilassati. Per ora copriremo la forma più elementare di illuminazione dinamica (dato che l'intero soggetto è enorme, e altri sono riusciti a riempire interi libri sul concetto).

Nello specifico, costruiremo un sistema di illuminazione dinamica a raggio singolo, singolo, fisso, che ci consentirà di iniziare a dilettarci sull'argomento. Prima di entrare in questo, diamo un'occhiata ad alcune delle nostre lezioni precedentemente fatte che useremo.


Ricapitolare

La nostra illuminazione dinamica verrà gestita punto per punto in quanto sono predisposte per essere disegnate sullo schermo. Ciò significa che faremo ampio uso di due delle nostre classi precedenti: il Punto classe e il telecamera classe. 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

Usando queste informazioni, mettiamo insieme il nostro corso base di illuminazione.


La nostra classe di illuminazione

Un esempio di illuminazione dinamica. Fonte: http://redeyeware.zxq.net

La nostra classe di illuminazione avrà bisogno di alcune cose per renderla funzionale - vale a dire una posizione, un colore, un tipo e un'intensità (o raggio di illuminazione).

Come ho detto prima, la nostra illuminazione verrà calcolata punto per punto prima di ogni punto che viene disegnato. L'aspetto positivo di questo è che è più semplice per come abbiamo organizzato il nostro motore, e anche che sposta più carico del nostro programma sul processore del sistema. Se dovessi precalcolare la tua illuminazione, sposterebbe invece il carico sul disco rigido del tuo sistema e, a seconda di come è progettato il tuo motore, potrebbe essere più semplice o più complesso.

Con tutto ciò in mente, la nostra classe potrebbe assomigliare a questo:

Classe di illuminazione Variabili: posizione num [3]; // (x, y, z) num rosso = 255; // cosa aggiungere al valore r di un punto alla massima intensità num verde = 255; // cosa aggiungere al valore g di un punto alla massima intensità num blu = 255; // cosa aggiungere al valore b di un punto alla stringa di intensità completa lightType = "point"; // il tipo di illuminazione raggio numerico = 50; // raggio della luce in pixel

Al momento lasceremo tutti i suoi valori codificati per semplicità, ma se si scopre che si desidera espandere la funzionalità delle classi di illuminazione, si potrebbe facilmente avere ognuno dei valori modificabili attraverso le funzioni, un costruttore, ecc..

Tutta la matematica importante per la nostra illuminazione dinamica sta per accadere all'interno della nostra classe di fotocamere però, quindi diamo un'occhiata a questo.


Luci? Telecamera? Motore.

Un altro esempio di illuminazione dinamica. Fonte: http://blog.illuminatelabs.com/2010/04/hdr-and-baked-lighting.html

Ora aggiungeremo una nuova variabile alla nostra classe di fotocamere, che useremo per memorizzare la nostra sorgente di luce. Per ora, la variabile memorizzerà solo un'istanza di illuminazione, ma potrebbe essere facilmente ridimensionata per consentire più punti luce.

Poco prima che venga tracciato un punto, controlleremo se è nel raggio della nostra luce. Una volta che sappiamo che è all'interno del raggio di luce, allora dobbiamo trovare la distanza tra il punto e la posizione della luce, e quindi dobbiamo regolare il colore del punto in base a quella distanza.

Tenendo tutto questo in mente, possiamo aggiungere un codice simile a quello della nostra fotocamera drawScene () funzione:

if (currentPoint.x> = (light.x - light.radius)) // se il punto è entro il limite sinistro della luce if (currentPoint.x <= (light.x + light.radius)) //if the point is within the light's right bound if(currentPoint.y >= (light.y - light.radius)) // se il punto è entro il limite superiore della luce se (currentPoint.y <= (light.y + light.radius)) //if the point is within the light's bottom bound //calculate distance between point and light (distance) //calculate percentage of light to apply (percentage = distance / radius) point.red += (light.red * percentage); //add the light's red, scaled to distance point.green += (light.green * percentage); //add the light's green, scaled to distance point.blue += (light.blue * percentage); //add the light's blue, scaled to distance    

Come puoi vedere, il nostro metodo di regolazione del colore di un punto al momento non è poi così avanzato (ce ne sono molti, se vuoi usarli invece). A seconda della distanza di un punto dal centro della luce, si alleggerisce il suo colore di una percentuale. Il nostro metodo di illuminazione non tiene conto dell'ombreggiamento, al momento, quindi le aree lontane dalla luce non diventeranno più scure e gli oggetti non bloccheranno la luce da altri oggetti che potrebbero trovarsi dietro di loro.


Segui la luce

Per il nostro programma questa volta, avremo alcune forme pre-renderizzate sullo schermo. Questi possono essere qualsiasi cosa tu voglia, ma per il nostro esempio, userò solo alcuni punti semplici. Ora, quando un utente fa clic in qualsiasi punto dello schermo, creeremo una sorgente di luce in quel punto. La prossima volta che fanno clic, sposteremo il punto in quella posizione e così via. Questo ci permetterà di vedere la nostra illuminazione dinamica in azione!

Ecco come potrebbe essere il tuo 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; // disegna gli oggetti iniziali e li imposta nello spazio della camera mentre (key! = esc) if (mouseClick) if (firstClick) // crea l'oggetto luce iniziale al posto del mouse else // modifica la posizione dell'oggetto luminoso  camera.drawScene (); 

Ora dovresti essere in grado di sperimentare la tua illuminazione dinamica in azione, e spero di vedere quanta più profondità possa aggiungere a un motore di gioco. Guarda la mia demo qui - usa il UN e S le chiavi per ridimensionare e il Y, H, U, J, io e K i tasti per ruotare.


Conclusione

Mentre la nostra illuminazione dinamica è semplice, può certamente essere migliorata se ti senti incline a farlo. Alcune cose che sarebbero piuttosto interessanti e anche abbastanza semplici da aggiungere sono:

  • raggio di illuminazione regolabile
  • colore della luce regolabile (invece di schiarire uniformemente un colore, schiarirlo di una frazione del colore impostato)
  • bloccare la luce dal viaggio oltre gli oggetti solidi
  • gestire più punti di luce
  • aggiungi un'ombra a tutti i punti al di fuori del raggio della luce
  • sperimentare con altre forme di luce (direzionale, cono, ecc.)

Grazie per aver verificato la nostra serie, Costruiamo un motore di gioco 3D. È stato bello scrivere questi articoli e ricorda che, se hai qualche domanda, non esitare a chiedere loro nei commenti qui sotto!

Puoi inoltre ottenere ulteriore assistenza su Envato Studio, dove puoi trovare numerosi fantastici servizi di progettazione e modellazione 3D a prezzi accessibili. 

Servizi di progettazione e modellazione 3D su Envato Studio