La oss bygge en 3D Graphics Engine Spaces and Culling

Velkommen! Dette er den tredje delen av serien vår på 3D grafikkmotorer. Hvis du gjorde det så langt inn i serien, vil du være glad for å vite at dette stykket vil bli mye lettere på matematisk aspekt av 3D-motorer, og i stedet vil fokusere på mer praktiske ting - spesielt å legge til et kamera og et grunnleggende gjengisystem.

Tips: Hvis du ikke har lest de to første delene ennå, foreslår jeg sterkt at du gjør det før du fortsetter.

Du kan også få ekstra hjelp på Envato Studio, hvor du kan velge blant et bredt spekter av 3D-design og modellering av høy kvalitet fra erfarne leverandører. 

3D Design & Modeling-tjenester på Envato Studio

oppsummering

Først av, la oss ta en titt på klassene som vi har opprettet så langt:

Point Class Variables: num tuple [3]; // (x, y, z) Operatører: Point AddVectorToPoint (Vector); Punkt SubtraherVectorFromPoint (Vector); Vector SubtractPointFromPoint (punkt); Null SetPointToPoint (punkt); // flytte punkt til spesifisert punkt Funksjoner: drawPoint; // tegne et punkt i sin posisjon tuple Vector Class Variables: num tuple [3]; // (x, y, z) Operatører: Vector AddVectorToVector (Vector); Vector SubtractVectorFromVector (Vector); Vector RotateXY (grader); Vector RotateYZ (grader); Vector RotateXZ (grader); Vector Scale (s0, s1, s2); // params: skalering langs hver akse

Bruke disse to klassene på egen hånd har vist seg litt rotete så langt, og tegning av alle mulige poeng kan drenere systemets minne ganske raskt. For å løse disse problemene, skal vi introdusere en ny klasse i vår spillmotor: The kamera.

Vårt kamera kommer til å være der all vår gjengivelse skjer, utelukkende; det kommer til Cull alle våre objekter på skjermen, og det skal også håndtere en liste over alle våre poeng.

Men før vi kan komme til alt dette, må vi først snakke litt om culling.


London Culling

Culling, per definisjon, er valget av objekter fra en større gruppe objekter. For vår spillmotor, vil det lille utvalget vi tar være poengene vi ønsker å tegne på skjermen. Den større gruppen av objekter vil være hvert eneste punkt som eksisterer.

Å gjøre dette reduserer drastisk av motorens avløp i systemets minne, ved å tegne bare hva en spiller faktisk kan se, i stedet for en hel verdens verdier av poeng. I vår motor skal vi gjøre dette ved å sette parametere for a se plass.

Vårt rom vil bli definert over alle tre av de tradisjonelle aksene: x, y og z. Dens x-definisjon vil bestå av alt mellom vinduets venstre og høyre grenser, dets y-definisjon vil bestå av alt mellom vinduets topp- og bunngrenser, og dets z-definisjon vil være mellom 0 (hvor kameraet er satt) og spillerens visningsavstand (for vår demonstrasjon, vil vi bruke en vilkårlig verdi av 100).

Før du tegner et punkt, vil kameraklassen sjekke for å se om dette punktet ligger innenfor vårt rom. Hvis det gjøres, vil punktet bli trukket; ellers vil det ikke.


Kan vi få noen kameraer her?

Med den grunnleggende forståelsen av culling kan vi utlede at vår klasse vil se slik ut, så langt:

Kamera klasse Vars: int minX, maxX; // minimum og maksimumsgrenser for X int minY, maxY; // minimum og maksimumsgrenser for Y int minZ, maxZ; // minimum og maksimumsgrenser for Z

Vi skal også få kameraet vårt til å håndtere all rendering for vår motor også. Avhengig av motoren, vil du oppdage at renderere ofte skilles fra kamerasystemene. Dette gjøres vanligvis for å holde systemene innkapslet pent, siden de to kunne bli ganske rotete hvis de holdes sammen, avhengig av motorens omfang. For våre formål vil det imidlertid være enklere å behandle dem som en.

For det første skal vi ha en funksjon som kan kalles eksternt fra klassen som vil tegne scenen. Denne funksjonen vil sykle gjennom hvert av punktene som eksisterer, sammenligne dem med kameraets culling-parametere, og tegne dem hvis det er aktuelt.


Kilde: http://en.wikipedia.org/wiki/File:ViewFrustum.svg

Tips: Hvis du ønsket å skille kamerasystemet fra rendereren, kan du bare lage en renderer klasse, må kamerasystemet kaste poengene, lagre de som skal tegnes i en matrise, og send deretter det aktuelle arrayet til tegne() funksjon av rendereren din.


Point Management

Det endelige stykket av kameraklassen vår kommer til å være dets punktstyringssystem. Avhengig av hvilket programmeringsspråk du bruker, kan dette bare være et enkelt utvalg av alle objekter som kan trekkes (vi skal håndtere mer enn bare poeng i senere deler). Alternativt må du kanskje bruke språkkursens standardobjekt for foreldre. Hvis du er super uheldig, må du opprette din egen gjenstandsforeldersklasse og ha hver trekkbar klasse (så langt bare poeng) være et barn av den klassen.

Etter å ha lagt det inn i klassen, ville en grunnleggende oversikt over kameraet se slik ut:

Kamera klasse Vars: int minX, maxX; // minimum og maksimumsgrenser for X int minY, maxY; // minimum og maksimumsgrenser for Y int minZ, maxZ; // minimum og maksimumsgrenser for Z array objectsInWorld; // et utvalg av alle eksisterende objekter Funksjoner: null drawScene (); // trekker alle nødvendige objekter til skjermen, returnerer ikke noe

Med disse tilleggene, la oss forbedre litt på det programmet vi laget sist.


Større og bedre ting

Vi skal lage et enkelt poeng tegneprogram, med prøveprogrammet som vi opprettet sist gang som utgangspunkt.

I denne iterasjonen av programmet skal vi legge til i bruk av vår nye kameraklasse. Når D tasten blir trykket, vil programmet redraw skjermen uten å kaste ut, og viser antall objekter som ble gjengitt i øverste høyre hjørne av skjermen. Når C Nøkkelen trykkes, programmet vil omdanne skjermen med kulling, og viser også antall gjengitte objekter.

La oss ta en titt på koden:

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 camera.objectsInWorld [100]; // lage 100 objekt mellomrom i kameraets array // sett kameraets visning space camera.minX = 0; camera.maxX = screenWidth; kamera.minY = 0; camera.maxY = screenHeight; camera.minZ = 0; camera.maxZ = 100; for (int x = 0; x < camera.objectsInWorld.length; x++)  //Set its location to a random point on the screen camera.objectsInWorld[x].tuple = [random(-200,1000), random(-200,1000), random(-100,200));  function redrawScreenWithoutCulling() //this function clears the screen and then draws all of the points  ClearTheScreen(); //use your Graphics API's clear screen function for(int x = 0; x < camera.objectsInWorld.length; x++)  camera.objectsInWorld[x].drawPoint(); //draw the current point to the screen   while(esc != pressed) // the main loop  if(key('d') == pressed)  redrawScreenWithoutCulling();  if(key('c') == pressed)  camera.drawScene();  if(key('a') == pressed)  Point origin = new Point(0,0,0); Vector tempVector; for(int x = 0; x < camera.objectsInWorld.length; x++)  //store the current vector address for the point, and set the point tempVector = camera.objectsInWorld[x].subtractPointFromPoint(origin); //reset the point so that the scaled vector can be added camera.objectsInWorld[x].setPointToPoint(origin); //scale the vector and set the point to its new, scaled location camera.objectsInWorld[x].addVectorToPoint(tempVector.scale(0.5,0.5,0.5));   if(key('s') == pressed)  Point origin = new Point(0,0,0); //create the space's origin as a point Vector tempVector; for(int x = 0; x < camera.objectsInWorld.length; x++)  //store the current vector address for the point, and set the point tempVector = camera.objectsInWorld[x].subtractPointFromPoint(origin); //reset the point so that the scaled vector can be added camera.objectsInWorld[x].setPointToPoint(origin); //scale the vector and set the point to its new, scaled location camera.objectsInWorld[x].addVectorToPoint(tempVector.scale(2.0,2.0,2.0));   if(key('r') == pressed)  Point origin = new Point(0,0,0); //create the space's origin as a point Vector tempVector; for(int x = 0; x < camera.objectsInWorld.length; x++)  //store the current vector address for the point, and set the point tempVector = camera.objectsInWorld[x].subtractPointFromPoint(origin); //reset the point so that the scaled vector can be added camera.objectsInWorld[x].setPointToPoint(origin); //scale the vector and set the point to its new, scaled location camera.objectsInWorld[x].addVectorToPoint(tempVector.rotateXY(15));    

Nå kan du se, førstehånds, kraften til å kaste! Vær oppmerksom på at hvis du ser gjennom utvalgskoden, gjøres noen ting litt annerledes for å gjøre demoene mer webvennlige. (Du kan sjekke ut min enkle demo her.)


Konklusjon

Med et kamera og rendering system under beltet ditt, kan du teknisk si at du har opprettet en 3D-spillmotor! Det kan ikke være altfor imponerende ennå, men det er på vei.

I vår neste artikkel ser vi på å legge til noen geometriske former i motoren vår (nemlig linjesegmenter og sirkler), og vi vil snakke om algoritmer som kan brukes til å passe sine ligninger til piksler på en skjerm.