Bine ati venit! Aceasta este a treia parte a seriei noastre despre motoarele grafice 3D. Dacă ați reușit atât de mult în serie, veți fi bucuroși să știți că această piesă va fi mult mai ușoară în ceea ce privește aspectul matematic al motoarelor 3D și în schimb va pune accent pe lucruri mai practice - în special, adăugând o cameră și un sistem de redare de bază.
Bacsis: Dacă nu ați citit încă primele două părți, vă sugerez să faceți înainte de a continua.
De asemenea, puteți obține ajutor suplimentar la Envato Studio, unde puteți alege dintr-o gamă largă de servicii de design și modelare 3D de înaltă calitate de la furnizori cu experiență.
Proiectare și modelare 3D pe Envato StudioÎn primul rând, să aruncăm o privire la clasele pe care le-am creat până acum:
Clasa de puncte Variabile: număr [3]; // (x, y, z) Operatori: Point AddVectorToPoint (Vector); Punctul SubtractVectorFromPoint (Vector); Vector SubtractPointFromPoint (punct); Null SetPointToPoint (punct); // mutați punctul la punctul specificat Funcții: drawPoint; // desenați un punct la tupla sa de poziție Clasa Vector Variables: num tuple [3]; // (x, y, z) Operatori: Vector AddVectorToVector (Vector); Vector SubtractVectorFromVector (Vector); Vector RotateXY (grade); Vector RotateYZ (grade); Vector RotateXZ (grade); Vector Scară (s0, s1, s2); // params: scalarea de-a lungul fiecărei axe
Folosirea acestor două clase pe cont propriu sa dovedit a fi puțin dezordonată până acum, și desenarea tuturor punctelor posibile poate duce la scurgerea memoriei sistemului într-un mod destul de rapid. Pentru a rezolva aceste probleme, vom introduce o nouă clasă în motorul nostru de jocuri: aparat foto.
Camera noastră va fi în cazul în care toate redarea noastră se întâmplă, exclusiv; merge spicui toate obiectele noastre pe ecran, și, de asemenea, va gestiona o listă a tuturor punctelor noastre.
Dar înainte de a ajunge la toate acestea, mai întâi trebuie să vorbim puțin despre sacrificare.
Înlăturarea, prin definiție, a selecției obiectelor dintr-un grup mai mare de obiecte. Pentru motorul nostru de joc, selecția mică pe care o luăm va fi punctul pe care vrem să-l atragem pe ecran. Grupul mai mare de obiecte va fi fiecare punct care există.
Făcând acest lucru, reduceți drastic puterea motorului pe sistemul de memorie, desenând doar ceea ce un jucător este de fapt capabil să vadă, mai degrabă decât un întreg de lume în valoare de puncte. În motorul nostru, vom face acest lucru prin setarea parametrilor pentru a vizualizați spațiul.
Spațiul nostru vizual va fi definit în toate cele trei axe tradiționale: x, y și z. Definiția sa x va consta în totul dintre limitele stângii și ale ferestrei ferestrei, definiția sa va consta în totul dintre limitele superioare și inferioare ale ferestrei, iar definiția lui z va fi între 0
(unde camera este setată) și distanța de vizualizare a jucătorului (pentru demonstrația noastră, vom folosi o valoare arbitrară de 100
).
Înainte de a desena un punct, clasa camerei noastre va verifica dacă acest punct se află în spațiul nostru de vizualizare. În caz contrar, punctul va fi tras; altfel, nu va.
Cu această înțelegere de bază a sacrificării, putem deduce că clasa noastră va arăta astfel:
Clasa camerei Vars: int minX, maxX; // limitele minime și maxime ale lui X int minY, maxY; // limitele minime și maxime ale lui Y int minZ, maxZ; // limitele minime și maxime ale lui Z
De asemenea, vom avea camera noastră să se ocupe și de performanțele pentru motorul nostru. În funcție de motor, veți găsi că redactorii sunt adesea separați de sistemele de cameră. Acest lucru se face de obicei pentru a păstra sistemele încapsulate frumos, deoarece - în funcție de domeniul motorului dvs. - cei doi ar putea deveni destul de dezordonați dacă sunt păstrați împreună. Cu toate acestea, pentru scopurile noastre, este mai simplu să le tratăm ca pe una.
În primul rând, vom dori o funcție care poate fi numită extern din clasa care va atrage scena. Această funcție va circula prin fiecare dintre punctele existente, le va compara cu parametrii de sacrificare al camerei și va fi extras dacă este cazul.
Bacsis: Dacă doriți să vă separați sistemul de cameră de la redactorul dvs., puteți să creați pur și simplu a Renderer
, sistemul de cameră trebuie să elimine punctele, să stocheze cele care urmează să fie desenate într-o matrice și apoi să trimită matricea la a desena()
funcția renderer dvs..
Ultima piesă a clasei camerei noastre va fi sistemul său de management al punctelor. În funcție de limba de programare pe care o folosiți, aceasta ar putea fi o simplă matrice a tuturor obiectelor care pot fi desenate (vom lucra mai mult decât punctele mai târziu). În mod alternativ, poate fi necesar să utilizați clasa părinte implicită a limbii. Dacă sunteți foarte ghinion, va trebui să vă creați propria clasă de părinte obiect și să aveți fiecare clasă trasabilă (până acum doar puncte) să fie un copil de acea clasă.
După adăugarea acestui aspect în clasă, o prezentare generală a camerei noastre ar arăta astfel:
Clasa camerei Vars: int minX, maxX; // limitele minime și maxime ale lui X int minY, maxY; // limitele minime și maxime ale lui Y int minZ, maxZ; // limitele minime și maxime ale obiectelor din array ZInWorld; // o matrice a tuturor obiectelor existente Funcții: null drawScene (); // atrage toate obiectele necesare pe ecran, nu returnează nimic
Cu aceste adăugiri, să îmbunătățim puțin programul pe care l-am făcut ultima oară.
Vom crea un program de desen simplu, cu programul de probă pe care l-am creat ultima dată ca punct de plecare.
În această iterație a programului, vom adăuga utilizarea noii noastre clase de camere. Cand D este apăsată tasta, programul va reprograma ecranul fără a fi tăiat, afișând numărul de obiecte care au fost redate în colțul din dreapta sus al ecranului. Cand C este apăsată tasta, programul va reprograma ecranul cu junghiul, afișând și numărul de obiecte redate.
Să aruncăm o privire la cod:
main // setup pentru grafica API preferată aici // configurare pentru introducerea tastaturii (poate să nu fie necesară) aici var camera = new Camera (); // a crea o instanță a camerei de cameră camera.objectsInWorld [100]; // creați 100 de spații de obiecte în interiorul matricei camerei // setați camera de vizualizare a camerei camera.minX = 0; camera.maxX = ecranWidth; camera.minY = 0; camera.maxY = screenHeight; camera.minZ = 0; camera.maxZ = 100; pentru (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));
Acum puteți vedea, la prima vedere, puterea de sacrificare! Rețineți că, dacă analizați codul eșantion, unele lucruri se fac puțin diferit pentru a face demo-urile mai prietenoase pe web. (Puteți să vedeți demo-ul meu simplu aici).
Cu un aparat de fotografiat și un sistem de redare sub centură, puteți spune tehnic că ați creat un motor de joc 3D! Poate că nu este încă prea impresionantă, dar este pe drum.
În următorul articol, vom examina adăugarea unor forme geometrice la motorul nostru (și anume segmente de linie și cercuri) și vom vorbi despre algoritmii care pot fi utilizați pentru a se potrivi ecuațiilor lor cu pixelii unui ecran.