Bine ați venit în cea de-a doua parte a seriei noastre 3D Graphics Engine! De data aceasta vom vorbi transformări liniare, care ne va permite să modificăm proprietățile precum rotația și scalarea vectorilor noștri și să ne uităm la modul de aplicare a acestora la clasele pe care le-am construit deja.
Dacă nu ați citit deja prima parte a acestei serii, vă sugerez să faceți acest lucru acum. Doar în cazul în care nu vă amintiți, aici este o recapitulare rapidă a ceea ce am creat ultima oară:
Clasa de puncte Variabile: număr [3]; // (x, y, z) Operatori: Point AddVectorToPoint (Vector); Punctul SubtractVectorFromPoint (Vector); SubtractPointFromPoint (punct); Funcții: // trageți un punct în poziția sa tuple cu graficul preferat API drawPoint; Clasa Vector Variables: num tuple [3]; // (x, y, z) Operatori: Vector AddVectorToVector (Vector); Vector SubtractVectorFromVector (Vector);
Aceste două clase vor sta la baza întregului motor grafic, unde primul reprezintă un punct (o locație fizică în spațiul dvs.), iar al doilea reprezintă un vector (spațiul / mișcarea între două puncte).
Pentru discuția noastră despre transformările liniare, ar trebui să faceți o mică schimbare la clasa Point: în loc să trimiteți date pe o linie de consolă ca înainte, utilizați graficul API preferat și să aveți funcția să desenați punctul curent pe ecran.
Doar un avertisment: Ecuațiile liniare de transformare arată mult mai rău decât sunt de fapt. Vor fi implicate unele trigonometre, dar nu trebuie să știți de fapt Cum pentru a face acea trigonometrie: voi explica ce trebuie să dați fiecărei funcții și ce veți ieși, iar pentru lucrurile între voi puteți folosi orice calculator sau o bibliotecă de matematică pe care ați putea avea.
Bacsis: Dacă doriți să înțelegeți mai bine funcționarea interioară a acestor ecuații, atunci ar trebui să urmăriți acest videoclip și să citiți acest PDF.Toate transformările liniare iau această formă:
\ [B = F (A) \]
Aceasta afirmă că dacă aveți o funcție de transformare liniară \ (F () \), iar intrarea dvs. este vectorul \ (A \), atunci ieșirea va fi vectorul \ (B \).
Fiecare dintre aceste piese - cei doi vectori și funcția - poate fi reprezentată ca o matrice: vectorul \ (B \) ca o matrice 1x3, vectorul \ (A \) ca o altă matrice 1x3 și transformarea liniară \ \) ca o matrice 3x3 (a matricea de transformare).
Aceasta înseamnă că, atunci când extindeți ecuația, se arată astfel:
\ [
\ Begin bmatrix
b_ 0 \\
b_ 1 \\
B_ 2
\ End bmatrix
=
\ Begin bmatrix
f_ 00 & f_ 01 & f_ 02 \\
f_ 10 & f_ 11 & f_ 12 \\
f_ 20 & f_ 21 & f_ 22
\ End bmatrix
\ Begin bmatrix
a_ 0 \\
a_ 1 \\
a_ 2
\ End bmatrix
\]
Dacă ați luat vreodată o clasă în trigonometrie sau algebră liniară, probabil că ați început să vă amintiți coșmarul care a fost matricea matematică. Din fericire, există o modalitate mai simplă de a scrie această ecuație pentru a lua cea mai mare parte a problemei din ea. Se pare ca aceasta:
\ [
\ Begin bmatrix
B_ 0 \\
B_ 1 \\
B_ 2
\ End bmatrix
=
\ Begin bmatrix
f_ 00 a_ 0 + f_ 01 a_ 1 + f_ 02 a_ 2 \\
f_ 10 a_ 0 + f_ 11 a_ 1 + f_ 12 a_ 2 \\
f_ 20 a_ 0 + f_ 21 a_ 1 + f_ 22 a_ 2 \\
\ End bmatrix
\]
Totuși, aceste ecuații pot fi modificate prin faptul că au o a doua intrare, cum ar fi în cazul rotațiilor, unde trebuie să fie date ambele vectori și cantitatea lor de rotație. Să aruncăm o privire la modul în care funcționează rotația.
O rotație este, prin definiție, o mișcare circulară a unui obiect în jurul unui punct de rotație. Punctul de rotație pentru spațiul nostru poate fi una dintre cele trei posibilități: fie planul XY, planul XZ, fie planul YZ (în cazul în care fiecare plan este alcătuit din doi vectori de bază despre care am discutat în prima parte a seriei ).
Cele trei puncte de rotație ale noastre înseamnă că avem trei matrice separate de rotație, după cum urmează:
XY rotație matrice:
\ [
\ Begin bmatrix
cos \ theta & -sin \ theta & 0 \\
sin \ theta & cos \ theta & 0 \\
0 & 0 & 1 \\
\ End bmatrix
\]
XZ rotație matrice:
\ [
\ Begin bmatrix
cos \ theta & 0 & sin \ theta \\
0 & 1 & 0 \\
-sin_theta & 0 & cos \ theta
\ End bmatrix
\]
Matricea de rotație YZ:
\ [
\ Begin bmatrix
1 & 0 & 0 \\
0 & cos \ theta & -sin \ theta \\
0 & sin \ theta & cos \ theta
\ End bmatrix
\]
Deci, pentru a roti un punct \ (A \) în jurul planului XY cu 90 de grade (\ (\ pi / 2 \) radiani - cele mai multe biblioteci de matematică au o funcție pentru conversia gradelor în radiani), urmați acești pași:
\ [
\ Begin aliniat
\ Begin bmatrix
B_ 0 \\
B_ 1 \\
B_ 2
\ End bmatrix
& =
\ Begin bmatrix
cos \ frac \ pi 2 & -sin \ frac \ pi 2 & 0 \\
sin \ frac \ pi 2 & cos \ frac \ pi 2 & 0 \\
0 & 0 & 1
\ End bmatrix
\ Begin bmatrix
a_ 0 \\
a_ 1 \\
a_ 2
\ End bmatrix \\
& =
\ Begin bmatrix
cos \ frac \ pi 2 a_ 0 + -sin \ frac \ pi 2 a_ 1 + 0a_ 2
sin \ frac \ pi 2 a_ 0 + cos \ frac \ pi 2 a_ 1 + 0a_ 2
0a_ 0 + 0a_ 1 + 1a_ 2
\ End bmatrix \\
& =
\ Begin bmatrix
0a_ 0 + -1a_ 1 + 0a_ 2 \\
1a_ 0 + 0a_ 1 + 0a_ 2 \\
0a_ 0 + 0a_ 1 + 1a_ 2
\ End bmatrix \\
& =
\ Begin bmatrix
-a_ 1 \\
a_ 0 \\
a_ 2
\ End bmatrix
\ End aliniat
\]
Deci dacă punctul dvs. inițial \ (A \) a fost \ ((3,4,5) \), atunci punctul dvs. de ieșire \ (B \) ar fi \ ((- 4,3,5) \).
Ca exercițiu, încercați să creați trei noi funcții pentru Vector
clasă. Unul ar trebui să rotească vectorul în jurul planului XY, unul în jurul planului YZ și unul în jurul planului XZ. Funcțiile dvs. ar trebui să primească cantitatea dorită de grade pentru rotire ca intrare și să returneze un vector ca ieșire.
Debitul de bază al funcțiilor dvs. ar trebui să fie după cum urmează:
Scalarea este o transformare care fie mărește sau diminuează un obiect bazat pe o scală stabilită.
Efectuarea acestei transformări este destul de simplă (cel puțin în comparație cu rotația). O transformare de scalare necesită două intrări: a vector de intrare și a scalarea 3-tuple, care definește modul în care vectorul de intrare trebuie să fie scalat în raport cu fiecare dintre axele de bază ale spațiului.
De exemplu, în comanda de scalare \ ((s_ 0, s_ 1, s_ 2) \), \ (s_ 0 \) \) de-a lungul axei Y și \ (s_ 2 \) de-a lungul axei Z.
Matricea de transformare a scalării este următoarea (unde \ (s_ 0 \), \ (s_ 1 \) și \ (s_ 2 \) sunt elementele scalei 3-tuple):
\ [
\ Begin bmatrix
s0 & 0 & 0 \\
0 & s1 & 0 \\
0 & 0 & s2
\ End bmatrix
\]
Pentru a face vectorul de intrare A \ ((a_ 0, a_ 1, a_ 2) \) de doua ori mai mare de-a lungul axei X (adica, folosind o scalare 3-tuple \ 2, 1, 1) \)), matematica ar arata astfel:
\ [
\ Begin aliniat
\ Begin bmatrix
B_ 0 \\
B_ 1 \\
B_ 2
\ End bmatrix
& =
\ Begin bmatrix
s0 & 0 & 0 \\
0 & s1 & 0 \\
0 & 0 & s2
\ End bmatrix
\ Begin bmatrix
a_ 0 \\
a_ 1 \\
a_ 2
\ End bmatrix \\
& =
\ Begin bmatrix
2 & 0 & 0 \\
0 & 1 & 0 \\
0 & 0 & 1
\ End bmatrix
\ Begin bmatrix
a_ 0 \\
a_ 1 \\
a_ 2
\ End bmatrix \\
& =
\ Begin bmatrix
2a_ 0 + 0a_ 1 + 0a_ 2 \\
0a_ 0 + 1a_ 1 + 0a_ 2 \\
0a_ 0 + 0a_ 1 + 1a_ 2
\ End bmatrix \\
& =
\ Begin bmatrix
2a_ 0 \\
a_ 1 \\
a_ 2
\ End bmatrix
\ End aliniat
\]
Deci, dacă se dă vectorul de intrare \ (A = (3,4,0) \), atunci vectorul de ieșire \ (B \) ar fi \ ((6,4,0) \).
Ca un alt exercițiu, adăugați o nouă funcție la clasa vectorială pentru scalare. Această nouă funcție ar trebui să ia o scalare 3-tuple și să returneze un vector de ieșire.
Debitul de bază al funcțiilor dvs. ar trebui să fie după cum urmează:
y0 = x0 * s0; y1 = x1 * s1; y2 = x2 * s2
).Acum că aveți transformări liniare sub centură, să construim un mic program rapid pentru a vă arăta noi abilități. Vom face un program care atrage un grup de puncte pe ecran și apoi ne permite să le modificăm în ansamblu prin transformări liniare pe ele.
Înainte de a începe, vom dori, de asemenea, să adăugăm o altă funcție pentru noi Punct
clasă. Aceasta se va numi setPointToPoint ()
, și va stabili pur și simplu poziția punctului curent cu cea a punctului care îi este transmis. Acesta va primi un punct ca intrare și nu va întoarce nimic.
Iată câteva specificații rapide pentru programul nostru:
Clasele noastre actuale:
Clasa de puncte Variabile: număr [3]; // (x, y, z) Operatori: Point AddVectorToPoint (Vector); Punctul SubtractVectorFromPoint (Vector); Vector SubtractPointFromPoint (punct); // stabilește poziția punctului curent cu cea a punctului introdus Null SetPointToPoint (Point); Funcții: // trageți un punct în poziția sa tuple cu graficul preferat API drawPoint; 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);
Cu aceste specificații, să ne uităm la ce ar putea fi codul nostru:
main // configurare pentru API-ul grafic preferat aici // setare pentru introducerea tastaturii (poate să nu fie necesară) aici // creați un array de 100 puncte Point Array pointArray [100]; pentru (int x = 0; x < pointArray.length; x++) //Set its location to a random point on the screen pointArray[x].tuple = [random(0,screenWidth), random(0,screenHeight), random(0,desiredDepth)); //this function clears the screen and then draws all of the points function redrawScreen() //use your Graphics API's clear screen function ClearTheScreen(); for (int x = 0; x < pointArray.length; x++) //draw the current point to the screen pointArray[x].drawPoint(); // while the escape is not being pressed, carry out the main loop while (esc != pressed) // perform various actions based on which key is pressed if (key('d') == pressed) redrawScreen(); if (key('a') == pressed) //create the space's origin as a point Point origin = new Point(0,0,0); Vector tempVector; for (int x = 0; x < pointArray.length; x++) //store the current vector address for the point, and set the point tempVector = pointArray[x].subtractPointFromPoint(origin); //reset the point so that the scaled vector can be added pointArray[x].setPointToPoint(origin); //scale the vector and set the point to its new, scaled location pointArray[x].addVectorToPoint(tempVector.scale(0.5,0.5,0.5)); redrawScreen(); if(key('s') == pressed) //create the space's origin as a point Point origin = new Point(0,0,0); Vector tempVector; for (int x = 0; x < pointArray.length; x++) //store the current vector address for the point, and set the point tempVector = pointArray[x].subtractPointFromPoint(origin); //reset the point so that the scaled vector can be added pointArray[x].setPointToPoint(origin); //scale the vector and set the point to its new, scaled location pointArray[x].addVectorToPoint(tempVector.scale(2.0,2.0,2.0)); redrawScreen(); if(key('r') == pressed) //create the space's origin as a point Point origin = new Point(0,0,0); Vector tempVector; for (int x = 0; x < pointArray.length; x++) //store the current vector address for the point, and set the point tempVector = pointArray[x].subtractPointFromPoint(origin); //reset the point so that the scaled vector can be added pointArray[x].setPointToPoint(origin); //scale the vector and set the point to its new, scaled location pointArray[x].addVectorToPoint(tempVector.rotateXY(15)); redrawScreen();
Acum ar trebui să aveți un program mic și răcoros pentru a vă arăta toate tehnicile noi! Îmi poți verifica demo-ul simplu aici.
În timp ce cu siguranță nu am acoperit toate posibilele transformări liniare disponibile, micro-motorul nostru începe să se formeze.
Ca întotdeauna, există câteva lucruri care au fost lăsate în afara motorului pentru simplitate (și anume forfecare și reflecții în această parte). Dacă doriți să aflați mai multe despre aceste două tipuri de transformări liniare, puteți afla mai multe despre ele pe Wikipedia și legăturile aferente.
În următoarea parte a acestei serii, vom acoperi diferite spații de vizualizare și cum să sacrificăm obiecte aflate în afara punctului nostru de vedere.
Dacă aveți nevoie de ajutor suplimentar, mergeți la Envato Studio, unde puteți găsi o mulțime de servicii fantastice de 3D Design & Modeling. Acești furnizori experimentați vă pot ajuta cu o gamă largă de proiecte diferite, așa că răsfoiți furnizorii, citiți recenzii și evaluări și alegeți persoana potrivită pentru a vă ajuta.
Proiectare și modelare 3D pe Envato Studio