Welkom! Dit is het zesde deel van onze serie Let's Build a 3D Graphics Engine die de basisprincipes van 3D grafische systemen behandelt. Deze keer gaan we praten over kleur en hoe we deze kunnen toevoegen aan onze bestaande klassen. We gaan ook enkele handige functies maken om de verlichting gemakkelijker te maken, wat ons volgende en laatste deel zal zijn.
Het toevoegen van kleur aan onze objecten zal niet al te groot van een onderneming worden, dus de enige twee klassen waar we zwaar op zullen focussen, zijn onze puntenklasse en onze cameraklasse. Als een opfriscursus, hier is hoe ze eruit zien:
Puntklasse variabelen: num tuple [3]; // (x, y, z) Operators: Point AddVectorToPoint (Vector); Point SubtractVectorFromPoint (Vector); Vector SubtractPointFromPoint (punt); Null SetPointToPoint (punt); Functies: drawPoint; // teken een punt op zijn positie tuple Cameraklasse Vars: int minX, maxX; intIJp, maxY; int minZ, maxZ; array objectsInWorld; // een array van alle bestaande objecten Functies: null drawScene (); // tekent alle benodigde objecten op het scherm
Tot nu toe heeft onze theoretische engine bijna alle basisprincipes op zijn plaats, waaronder:
Punt
en Vector
klassen (de bouwstenen van onze motor).Camera
class (stelt ons viewport in en verwijdert punten buiten het scherm).Laten we nu wat kleur toevoegen!
Onze engine gaat kleuren verwerken door hun waarden in de engine op te slaan Punt
klasse. Hierdoor kan elk punt zijn eigen individuele kleur hebben, waardoor berekeningen voor belichting en schaduw aanzienlijk eenvoudiger worden (voor mensen tenminste, soms is het minder efficiënt om een engine op deze manier te coderen). Bij het uitzoeken van de belichting of schaduw van een scène, kunnen we de functie eenvoudig een puntenlijst geven en vervolgens door elk van hen heen koelen, gebruikmakend van hun afstand tot het licht om hun kleur dienovereenkomstig aan te passen.
Een van de meest gebruikelijke manieren om kleur in programmeren op te slaan, is door rode, groene en blauwe waarden te gebruiken om de werkelijke gewenste kleur te maken (dit wordt meestal additieve kleurmenging genoemd). Door in elk van deze kleursegmenten een waarde van 0-255 op te slaan, kunt u gemakkelijk een grote verscheidenheid aan kleuren maken. (Dit is hoe de meeste API's kleur bepalen, dus vanwege de compatibiliteitsreden is het logisch om deze methode te gebruiken).
Afhankelijk van de grafische API die u gebruikt, kunt u deze waarden vervolgens in een decimale vorm doorgeven (255,0,0
), of in hexadecimale vorm (0xFF0000
of # FF0000
). We gaan decimaal formaat gebruiken in onze engine omdat het een beetje gemakkelijker is om mee te werken. Als uw grafische API hexadecimale waarden gebruikt, heeft deze waarschijnlijk ook een functie voor het converteren van decimaal naar hexadecimaal, dus dit zou geen probleem moeten zijn.
Om onze kleurimplementatie gestart te krijgen, voegen we drie nieuwe variabelen toe aan onze Point-klasse: rood
, blauw
, en groen
. Er is nog niets te bizar aan de gang, maar hier is wat onze Punt
de nieuwe schets van de klas zou er als volgt uit kunnen zien:
Puntklasse variabelen: num tuple [3]; // (x, y, z) num rood, groen, blauw; // kan worden afgekort tot r, g, b indien gewenst. Operators: Point AddVectorToPoint (Vector); Point SubtractVectorFromPoint (Vector); Vector SubtractPointFromPoint (punt); Null SetPointToPoint (punt); Functies: drawPoint; // teken een punt op zijn positie tuple
Dat is alles wat we nodig hebben om de kleur van ons punt op te slaan. Nu moeten we gewoon de draw-functie van onze camera aanpassen zodat deze de opgegeven kleur gebruikt.
Dit gaat drastisch veranderen afhankelijk van welke grafische API je gebruikt, maar ze zouden allemaal een vergelijkbare functie als deze moeten hebben:
object.setColor (rood, groen, blauw)
Als uw grafische API hexadecimale waarden gebruikt voor kleuren in plaats van decimalen, ziet uw functie er ongeveer zo uit:
object.setColor (toHex (rood, groen, blauw))
Dat laatste stukje gebruikt een toHex ()
functie (nogmaals, de functienamen verschillen van API tot API) om een RGB-waarde in een hexadecimale waarde om te zetten, zodat u dit niet hoeft te doen.
Nadat u deze wijzigingen hebt aangebracht, zou u nu gekleurde punten in uw scène kunnen hebben. Om nog een stap verder te gaan, passen we elk van onze rasterklassen aan zodat onze hele vorm kan worden gekleurd.
Om dit aan onze klassen toe te voegen, hoeven we eenvoudig kleurafhandeling aan hun constructorfuncties toe te voegen. Dit kan er uit zien als:
lineSegment :: constructor (startX, startY, endX, endY, rood, groen, blauw) this.startX = startX; this.startY = startY; this.endX = endX; this.endY = endY; this.red = rood; this.green = groen; this.blue = blauw;
Dan moeten we alleen de functie retourpunten aanpassen, zodat het elk punt in zijn array instelt op de opgegeven kleur. De nieuwe functie ziet er als volgt uit:
function returnPointsInSegment () // maak een lijst om alle punten van het lijnsegment op te slaan var pointArray = new Array (); // stel de variabelen van deze functie in op basis van de begin- en eindpunten van de klasse var x0 = this.startX; var y0 = this.startY; var x1 = this.endX; var y1 = this.endY; // definieer vectorverschillen en andere variabelen die vereist zijn voor het algoritme van Bresenham var dx = Math.abs (x1-x0); var dy = Math.abs (y1-y0); var sx = (x0 & x1)? 1: -1; // step x var sy = (y0 & y1)? 1: -1; // step y var err = dx-dy; // haal de initiële foutwaarde // stel het eerste punt in de array pointArray.push in (nieuw punt (x0, y0, this.red, this.green, this.blue)); // Hoofdverwerkingslus terwijl (! ((X0 == x1) && (y0 == y1))) var e2 = err * 2; // de foutwaarde behouden // gebruik de foutwaarde om te bepalen of het punt naar boven of beneden moet worden afgerond als (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;
Nu moet elk punt binnen het lijnsegment dezelfde kleur zijn die in het lijnsegment is ingevoerd. Je kunt deze methode ook gebruiken om kleuren in te stellen in je andere rasterklassen, en binnenkort zal je scène tot leven komen met kleur!
Laten we onze nieuwe functies in actie brengen door een programma te maken om ze te laten zien.
Met behulp van additieve kleurmenging kunnen we eenvoudig meer dan 16,7 miljoen verschillende kleuren maken met alleen de eenvoudige (r, g, b
) notatie. We gaan een programma maken dat gebruik maakt van dit enorme aantal kleuren.
Met behulp van toetsaanslagen gaan we de gebruiker toestaan om de rode, groene en blauwe waarden van een object individueel te regelen, zodat ze in elke gewenste kleur kunnen worden geplaatst.
De specificaties voor ons programma zijn als volgt:
Met dat alles in gedachten, laten we eens kijken naar wat een basisschema van ons programma eruit zou kunnen zien:
main // setup voor je favoriete grafische API hier // setup voor toetsenbordinvoer (is mogelijk niet nodig) var camera = new Camera (); // maak een instantie van de cameraklasse // stel de cameraruimte van de beeldruimte in.minX = 0; camera.maxX = schermbreedte; camera.minY = 0; camera.maxY = schermhoogte; camera.minZ = 0; camera.maxZ = 100; // sla onze kleuren op zodat ze kunnen worden bewerkt var rood, groen, blauw; // begin object en stel het in op een variabele terwijl (toets! = esc) if (toetsaanslag = 'a') if (rood> 0) rood -; object.red = rood; // object opnieuw tekenen if (toets indrukken = 'q') if (rood < 255) red ++; object.red = red; //redraw object if(key press = 's') if(green > 0) groen -; object.green = groen; // object opnieuw tekenen if (toetsaanslag = 'w') if (groen < 255) green ++; object.green = green; //redraw object if(key press = 'd') if(blue > 0) blauw -; object.blue = blauw; // object opnieuw tekenen if (toetsindruk = 'e') if (blauw < 255) blue ++; object.blue = blue; //redraw object
Nu kunnen we met ons object spelen en het in elke gewenste kleur maken!
Bekijk de demo hier - druk herhaaldelijk op de Q, w, E, EEN, S, en D toetsen om de kleur van het vierkant te veranderen.
Met kleur toegevoegd aan onze motor, hebben we alles wat we nodig hebben om eindelijk wat verlichting aan te kunnen. In het volgende artikel zullen we kijken naar het creëren van lichtbronnen en enkele functies creëren om die bronnen de kleuren van onze punten te laten beïnvloeden. De diepte die verlichting toevoegt aan een motor is zeer bevredigend, dus zorg ervoor dat je het bekijkt!