Velkommen! Dette er den sjette delen i vår La oss bygge en 3D Graphics Engine-serie som dekker grunnleggende 3D grafikksystemer. Denne gangen skal vi snakke om farge og hvordan å legge den inn i våre eksisterende klasser. Vi skal også lage noen nyttige funksjoner for å gjøre håndteringsbelysningen enklere, som er hva vår neste og siste del skal omhandle.
Å legge til farge på objektene våre kommer ikke til å være for stor av et foretak, så den eneste to klassen som vi skal fokusere tungt på, er vår poengklasse og vår kameraklasse. Som en oppdatering, her er hvordan de ser ut:
Point Class Variables: num tuple [3]; // (x, y, z) Operatører: Point AddVectorToPoint (Vector); Punkt SubtraherVectorFromPoint (Vector); Vector SubtractPointFromPoint (punkt); Null SetPointToPoint (punkt); Funksjoner: drawPoint; // tegne et punkt på sin posisjon tuple Kamera klasse Vars: int minX, maxX; int minY, maxY; int minZ, maxZ; array objectsInWorld; // et utvalg av alle eksisterende objekter Funksjoner: null drawScene (); // trekker alle nødvendige objekter til skjermen
Hittil har vår teoretiske motor nesten alle grunnleggende på plass, inkludert:
Punkt
og Vector
klasser (byggeblokkene til motoren vår).Kamera
klasse (setter vår visningsport, og culls poeng utenfor skjermen).La oss legge til litt farge!
Motoren vår skal håndtere farger ved å lagre verdiene i sin Punkt
klasse. Dette gjør det mulig for hvert punkt å ha sin egen individuelle farge, noe som gjør belysning og skyggeberegninger mye enklere (for folk, i alle fall - noen ganger er det mindre effektivt å kode en motor på denne måten). Når vi regner ut en scenes belysning eller skygge, kan vi enkelt gi funksjonen en liste med punkter, og deretter kile gjennom hver av dem, ved å bruke avstanden fra lyset for å endre fargen deres tilsvarende.
En av de vanligste måtene å lagre farge i programmering er å bruke røde, grønne og blå verdier for å lage den aktuelle ønsket farge (dette kalles vanligvis additiv fargemiksing). Ved å lagre en verdi på 0-255 i hvert av disse fargesegmentene, kan du enkelt lage et bredt utvalg av farger. (Dette er hvordan de fleste APIer bestemmer farge, så av kompatibilitetsgrunnlag er det fornuftig å bruke denne metoden).
Deretter, avhengig av grafikk API som du bruker, kan du passere disse verdiene i enten desimalform (255,0,0
), eller i heksadesimal form (0xff0000
eller # FF0000
). Vi skal bruke desimalformat i vår motor siden det er litt lettere å jobbe med. Også, hvis grafikk-API bruker heksadesimale verdier, har det sannsynligvis en funksjon for å konvertere fra desimalt til heksadesimale, så dette bør ikke være et problem.
For å få vår fargeimplementering påbegynt, skal vi legge til i tre nye variabler til vår Point-klasse: rød
, blå
, og grønn
. Det er ikke noe for utlandsk på gang ennå, men her er hva vår Punkt
klassens nye oversikt kan se ut som:
Point Class Variables: num tuple [3]; // (x, y, z) num rød, grønn, blå; // kan forkortes til r, g, b hvis ønskelig Operatører: Point AddVectorToPoint (Vector); Punkt SubtraherVectorFromPoint (Vector); Vector SubtractPointFromPoint (punkt); Null SetPointToPoint (punkt); Funksjoner: drawPoint; // tegne et punkt på sin posisjon tuple
Det er alt vi trenger for å lagre punktets farge. Nå trenger vi bare å justere kameraets tegnefunksjon slik at den bruker den angitte fargen.
Dette kommer til å endre seg drastisk avhengig av hvilken grafikk-API du bruker, men de burde alle ha en lignende funksjon til dette:
object.setColor (rød, grønn, blå)
Hvis grafikk-APIen din skjer for å bruke heksadesimale verdier for farge i stedet for desimal, så vil funksjonen din se ut som denne:
object.setColor (toHex (rød, grønn, blå))
Den siste delen bruker a toHex ()
funksjonen (igjen vil funksjonsnavnene avvike fra API til API) for å konvertere en RGB-verdi til en heksadesimal verdi slik at du ikke trenger å.
Etter å ha gjort disse endringene, bør du nå kunne ha fargede poeng i din scene. For å ta det et skritt videre, skal vi justere hver av våre rasteriseringsklasser slik at hele vår form kan bli farget.
For å legge til dette i klassene våre, må vi ganske enkelt legge til fargebehandling til deres konstruktørfunksjoner. Dette kan se ut som:
lineSegment :: constructor (startX, startY, endX, endY, rød, grønn, blå) this.startX = startX; this.startY = startY; this.endX = endX; this.endY = endY; this.red = rød; this.green = green; this.blue = blue;
Da trenger vi bare å endre returpunktsfunksjonen slik at den setter hvert punkt i sin matrise for å ha den angitte fargen. Den nye funksjonen vil se slik ut:
funksjon returnPointsInSegment () // lage en liste for å lagre alle linjesegmentets punkter var pointArray = new Array (); // sett denne funksjonens variabler basert på klassens start- og sluttpunkter var x0 = this.startX; var y0 = this.startY; var x1 = this.endX; var y1 = this.endY; // definere vektorforskjeller og andre variabler som kreves for Bresenhams algoritme var dx = Math.abs (x1-x0); var dy = Math.abs (y1-y0); var sx = (x0 og x1)? 1: -1; // trinn x var sy = (y0 & y1)? 1: -1; // trinn y var err = dx-dy; // få den opprinnelige feilverdien // sett det første punktet i array pointArray.push (nytt punkt (x0, y0, this.red, this.green, this.blue)); // Hovedbehandlingssløyfen mens (! ((X0 == x1) && (y0 == y1))) var e2 = err * 2; // hold feilverdien // bruk feilverdien for å avgjøre om punktet skal avrundes opp eller ned hvis (e2 => -dy) err - = dy; x0 + = sx; hvis (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;
Nå skal hvert punkt i linjesegmentet være den samme fargen som ble sendt inn i linjesegmentet. Du kan bruke denne metoden til å sette farger opp i dine andre rasteriserende klasser også, og snart vil scenen bli levende med farge!
La oss sette våre nye funksjoner i handling ved å lage et program for å vise dem.
Ved å bruke additiv fargemiksing, kan vi enkelt lage over 16,7 millioner forskjellige farger med bare de enkle (r, g, b
) notasjon. Vi skal lage et program som utnytter dette store antallet farger.
Ved hjelp av nøkkelpresser, skal vi tillate brukeren å kontrollere en objekts røde, grønne og blå verdier enkeltvis, slik at de kan gjøre det til en hvilken som helst farge de ønsker.
Spesifikasjonene for vårt program er som følger:
Med alt dette i tankene, la oss ta en titt på hva en grunnleggende oversikt over vårt program kan se ut som:
main // setup for din favoritt grafikk-API her // oppsett for tastaturinngang (kanskje ikke nødvendig) her var kamera = nytt kamera (); // lage en forekomst av kameraklassen // sett kameraets visning space camera.minX = 0; camera.maxX = screenWidth; kamera.minY = 0; camera.maxY = screenHeight; camera.minZ = 0; camera.maxZ = 100; // lagre våre farger slik at de kan manipuleres var rød, grønn, blå; // tegne innledende objekt og sett det til en variabel mens (nøkkel! = esc) hvis (tast trykk = 'a') hvis (rødt> 0) rødt; object.red = rød; // redraw object hvis (tast trykk = 'q') hvis (rød < 255) red ++; object.red = red; //redraw object if(key press = 's') if(green > 0) grønn -; object.green = green; // redraw object hvis (tast trykk = 'w') hvis (grønn < 255) green ++; object.green = green; //redraw object if(key press = 'd') if(blue > 0) blå -; object.blue = blue; // redraw objekt hvis (tast trykk = 'e') hvis (blå < 255) blue ++; object.blue = blue; //redraw object
Nå kan vi leke med vårt objekt og gjøre det til enhver farge du ønsker!
Sjekk ut demoen her - trykk gjentatte ganger på Q, W, E, EN, S, og D taster for å endre firkantens farge.
Med farge lagt inn i motoren vår, har vi alt vi trenger på plass for å håndtere litt belysning. I neste artikkel vil vi se på å skape lyskilder, og skape noen funksjoner for å tillate disse kildene å påvirke våre poengfarger. Dybden som belysning legger til en motor, er ekstremt tilfredsstillende, så sørg for at du sjekker den ut!