Välkommen! Detta är den sjätte delen i vår Låt oss bygga en 3D Graphics Engine-serie som täcker grunderna i 3D grafiksystem. Den här gången kommer vi att prata om färg och hur man lägger till den i våra befintliga klasser. Vi ska också skapa några användbara funktioner för att göra hanteringsbelysningen enklare, vilket är vad vår nästa och sista del kommer att handla om.
Att lägga färg till våra objekt kommer inte att vara för stor för ett företag, så den enda två klassen som vi kommer att fokusera på är vår poängklass och vår kamera klass. Som en uppdatering är här hur de ser ut:
Point Class Variables: num tuple [3]; // (x, y, z) Operatörer: Peka AddVectorToPoint (Vector); Punkt SubtraherVectorFromPoint (Vector); Vector SubtractPointFromPoint (Punkt); Null SetPointToPoint (Point); Funktioner: drawPoint; // rita en punkt i sin position tuple Kameraklass Vars: int minX, maxX; int minY, maxY; int minZ, maxZ; array objectsInWorld; // en grupp av alla existerande objekt Funktioner: null drawScene (); // drar alla nödvändiga objekt till skärmen
Hittills har vår teoretiska motor nästan alla grunderna på plats, bland annat:
Punkt
och Vektor
klasser (byggstenarna i vår motor).Kamera
klassen (sätter vår visningsport, och culls pekar utanför skärmen).Låt oss nu lägga till lite färg!
Vår motor ska hantera färger genom att lagra sina värden inom sin Punkt
klass. Detta gör det möjligt för varje punkt att ha sin egen individuella färg, vilket gör belysning och skuggning mycket enklare (för människor, åtminstone - ibland är det mindre effektivt att koda en motor på så sätt). När vi bestämmer en scens belysning eller skuggning kan vi enkelt tillhandahålla funktionen med en lista med punkter och sedan snurra genom var och en av dem med hjälp av deras avstånd från ljuset för att ändra deras färg i enlighet med detta.
Ett av de vanligaste sätten att lagra färg i programmeringen är att använda röda, gröna och blåa värden för att skapa den önskade önskade färgen (det här kallas vanligtvis färgämneblandning). Genom att lagra ett värde på 0-255 i vart och ett av dessa färgsegment kan du enkelt skapa ett brett utbud av färger. (Så här bestämmer de flesta API: er färg, så av kompatibilitetsskäl är det meningsfullt att använda den här metoden).
Sedan kan du, beroende på grafik API som du använder, skicka dessa värden antingen i decimalform (255,0,0
) eller i hexadecimal form (0xFF0000
eller # FF0000
). Vi ska använda decimalformat i vår motor eftersom det är lite lättare att arbeta med. Om ditt grafik API använder sig av hexadecimala värden har det också en funktion för att konvertera från decimal till hexadecimal, så det borde inte vara ett problem.
För att få igång vår färgimplementering kommer vi att lägga till i tre nya variabler till vår Point-klass: röd
, blå
, och grön
. Det finns inget för outlandish pågår ännu, men här är vad vårt Punkt
klassens nya skiss kan se ut:
Point Class Variables: num tuple [3]; // (x, y, z) num röd, grön, blå; // kan förkortas till r, g, b om så önskas Operatörer: Peka AddVectorToPoint (Vector); Punkt SubtraherVectorFromPoint (Vector); Vector SubtractPointFromPoint (Punkt); Null SetPointToPoint (Point); Funktioner: drawPoint; // rita en punkt i sin position tuple
Det är allt vi behöver för att lagra vår punkts färg. Nu behöver vi justera kamerans dragningsfunktion så att den använder den angivna färgen.
Detta kommer att förändras drastiskt beroende på vilket grafik API du använder, men de ska alla ha en liknande funktion till detta:
object.setColor (röd, grön, blå)
Om ditt grafik API händer att använda hexadecimala värden för färg istället för decimal, så ser din funktion ut som här:
object.setColor (toHex (röd, grön, blå))
Den sista biten använder a toHex ()
funktionen (igen kommer funktionsnamnen att skilja sig från API till API) för att konvertera ett RGB-värde till ett hexadecimalt värde så att du inte behöver.
Efter att ha gjort dessa ändringar bör du nu kunna ha färgade poäng inom din scen. För att ta det ett steg längre, kommer vi att justera var och en av våra rasteriseringskurser så att hela vår form kan färgas.
För att lägga till detta i våra klasser måste vi helt enkelt lägga till färghantering i sina konstruktörsfunktioner. Det här kan se ut som:
lineSegment :: constructor (startX, startY, endX, endY, röd, grön, blå) this.startX = startX; this.startY = startY; this.endX = endX; this.endY = endY; this.red = röd; this.green = green; this.blue = blue;
Då behöver vi bara ändra dess returpunkter funktion så att den ställer in varje punkt i sin matris för att få den angivna färgen. Den nya funktionen skulle se ut så här:
funktionen returnPointsInSegment () // skapa en lista för att lagra alla linjesegmentets punkter var pointArray = new Array (); // ställa in den här funktionens variabler baserat på klassens start- och slutpunkter var x0 = this.startX; var y0 = this.startY; var x1 = this.endX; var y1 = this.endY; // definiera vektorskillnader och andra variabler som krävs för Bresenhams algoritm var dx = Math.abs (x1-x0); var dy = Math.abs (y1-y0); var sx = (x0 & x1)? 1: -1; // steg x var sy = (y0 & y1)? 1: -1; // steg y var err = dx-dy; // få det ursprungliga felvärdet // ställa in den första punkten i array pointArray.push (ny punkt (x0, y0, this.red, this.green, this.blue)); // Huvudbehandlingsslinga medan (! ((X0 == x1) && (y0 == y1))) var e2 = err * 2; // hålla felvärdet // använd felvärdet för att bestämma om punkten ska rundas upp eller ner om (e2 => -dy) err - = dy; x0 + = sx; om (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 ska alla punkter inom linjesegmentet vara samma färg som passerade in i linjesegmentet. Du kan använda den här metoden för att ställa in färger i dina andra rasteriseringsklasser, och snart kommer din scen att leva med färg!
Låt oss sätta in våra nya funktioner genom att göra ett program för att visa dem.
Med hjälp av additiv färgblandning kan vi enkelt skapa över 16,7 miljoner olika färger med bara de enkla (r, g, b
) notering. Vi ska skapa ett program som utnyttjar detta stora antal färger.
Med hjälp av tangenttryck kommer vi att tillåta användaren att styra ett objekts röda, gröna och blå värden individuellt, så att de kan göra det till en färg som de skulle vilja ha.
Specifikationerna för vårt program är som följer:
Med allt detta i åtanke, låt oss ta en titt på vad en grundläggande skiss av vårt program kan se ut:
main // setup för ditt favoritgrafik API här // inställning för tangentbordsinmatning (kanske inte krävs) här var kamera = ny kamera (); // skapa en instans av kameraklassen // ställa in kamerans vy space camera.minX = 0; camera.maxX = screenWidth; camera.minY = 0; camera.maxY = screenHeight; camera.minZ = 0; camera.maxZ = 100; // lagra våra färger så att de kan manipuleras var rött, grönt, blått; // rita initialt objekt och sätt det till en variabel medan (tangent! = esc) om (tangenttryck = 'a') om (röd> 0) röd -; object.red = röd; // redraw object if (key press = 'q') om (röd < 255) red ++; object.red = red; //redraw object if(key press = 's') if(green > 0) grön -; object.green = green; // redraw object if (key press = 'w') om (grön < 255) green ++; object.green = green; //redraw object if(key press = 'd') if(blue > 0) blå -; object.blue = blue; // redraw object if (key press = 'e') om (blå < 255) blue ++; object.blue = blue; //redraw object
Nu kan vi leka med vårt föremål och göra det till någon färg som du önskar!
Kolla in demoen här - tryck upprepade gånger på Q, W, E, en, S, och D nycklar för att ändra torgets färg.
Med färg i vår motor har vi allt vi behöver för att slutligen hantera lite belysning. I nästa artikel kommer vi att titta på att skapa ljuskällor och skapa några funktioner för att tillåta dessa källor att påverka våra poängs färger. Djupet som belysningen ger till en motor är mycket tillfredsställande, så se till att du kolla in det!