Benvenuti nella seconda parte della nostra serie di 3D Graphics Engine! Questa volta parleremo di trasformazioni lineari, che ci permetterà di modificare proprietà come la rotazione e il ridimensionamento dei nostri vettori, e di vedere come applicarli alle classi che abbiamo già costruito.
Se non hai già letto la prima parte di questa serie, ti suggerisco di farlo ora. Nel caso in cui non ricordi, ecco un breve riepilogo di ciò che abbiamo creato l'ultima volta:
Point Class Variables: num tuple [3]; // (x, y, z) Operatori: Punto AddVectorToPoint (Vettore); Point SubtractVectorFromPoint (Vector); SubtractPointFromPoint (Point); Funzioni: // disegna un punto nella sua posizione tupla con il tuo drawPoint grafico API preferito; Classe vettoriale Variabili: num tuple [3]; // (x, y, z) Operatori: Vector AddVectorToVector (Vector); Vector SubtractVectorFromVector (Vector);
Queste due classi saranno la base del nostro intero motore grafico, dove il primo rappresenta un punto (una posizione fisica all'interno del tuo spazio) e il secondo rappresenta un vettore (lo spazio / movimento tra due punti).
Per la nostra discussione sulle trasformazioni lineari, dovresti fare una piccola modifica alla classe Point: invece di esportare i dati su una linea di console come prima, usa la tua API grafica preferita e la funzione disegna il punto corrente sullo schermo.
Solo un avvertimento: le equazioni di trasformazione lineare sembrano molto peggiori di quanto non siano in realtà. Ci sarà un po 'di trigonometria, ma non devi saperlo Come per fare quella trigonometria: ti spiegherò cosa devi dare a ciascuna funzione e cosa uscirai, e per le cose intermedie puoi semplicemente usare qualsiasi calcolatrice o libreria matematica che potresti avere.
Mancia: Se vuoi avere una migliore comprensione del funzionamento interno di queste equazioni, allora dovresti guardare questo video e leggere questo PDF.Tutte le trasformazioni lineari prendono questa forma:
\ [B = F (A) \]
Questo indica che se hai una funzione di trasformazione lineare \ (F () \), e il tuo input è il vettore \ (A \), allora il tuo output sarà il vettore \ (B \).
Ognuno di questi pezzi - i due vettori e la funzione - può essere rappresentato come una matrice: il vettore \ (B \) come una matrice 1x3, il vettore \ (A \) come un'altra matrice 1x3 e la trasformazione lineare \ (F \) come matrice 3x3 (a matrice di trasformazione).
Ciò significa che, quando espandi l'equazione, assomiglia a questo:
\ [
\ 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
\]
Se hai mai preso una lezione di trigonometria o di algebra lineare, probabilmente stai iniziando a ricordare l'incubo che era la matematica delle matrici. Fortunatamente, c'è un modo più semplice per scrivere questa equazione per prendere la maggior parte del problema. Sembra questo:
\ [
\ 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
\]
Tuttavia, queste equazioni possono essere alterate avendo un secondo input, come nel caso delle rotazioni, dove deve essere dato un vettore e la sua quantità di rotazione. Diamo un'occhiata a come funzionano le rotazioni.
Una rotazione è, per definizione, un movimento circolare di un oggetto attorno a un punto di rotazione. Il punto di rotazione del nostro spazio può essere una delle tre possibilità: o il piano XY, il piano XZ o il piano YZ (dove ciascun piano è costituito da due dei nostri vettori di base che abbiamo discusso nella prima parte della serie ).
I nostri tre punti di rotazione significano che abbiamo tre matrici di rotazione separate, come segue:
Matrice di rotazione XY:
\ [
\ Begin bmatrix
cos \ theta & -sin \ theta & 0 \\
sin \ theta & cos \ theta & 0 \\
0 & 0 & 1 \\
\ End bmatrix
\]
Matrice di rotazione XZ:
\ [
\ Begin bmatrix
cos \ theta & 0 & sin \ theta \\
0 & 1 & 0 \\
-sin \ theta & 0 & cos \ theta
\ End bmatrix
\]
Matrice di rotazione YZ:
\ [
\ Begin bmatrix
1 & 0 & 0 \\
0 & cos \ theta & -sin \ theta \\
0 e sin \ theta e cos \ theta
\ End bmatrix
\]
Quindi per ruotare un punto \ (A \) attorno al piano XY di 90 gradi (\ (\ pi / 2 \) radianti - la maggior parte delle librerie matematiche ha una funzione per convertire i gradi in radianti), segui questi passaggi:
\ [
\ Begin allineati
\ Begin bmatrix
b_ 0 \\
b_ 1 \\
b_ 2
\ End bmatrix
& =
\ Begin bmatrix
cos \ frac \ pi 2 & -sin \ frac \ pi 2 e 0 \\
sin \ frac \ pi 2 e cos \ frac \ pi 2 e 0 \\
0 e 0 e 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 allineato
\]
Quindi se il tuo punto iniziale \ (A \) era \ ((3,4,5) \), allora il tuo punto di uscita \ (B \) sarebbe \ ((- - 4,3,5) \).
Come esercizio, prova a creare tre nuove funzioni per il Vettore
classe. Uno dovrebbe ruotare il vettore attorno al piano XY, uno attorno al piano YZ e uno attorno al piano XZ. Le tue funzioni dovrebbero ricevere la quantità desiderata di gradi per la rotazione come input e restituire un vettore come output.
Il flusso di base delle tue funzioni dovrebbe essere il seguente:
Lo scaling è una trasformazione che ingrandisce o diminuisce un oggetto in base a una scala impostata.
L'esecuzione di questa trasformazione è abbastanza semplice (almeno rispetto alle rotazioni). Una trasformazione di ridimensionamento richiede due input: an vettore di input e a scalare 3-tupla, che definisce il modo in cui il vettore di input deve essere ridimensionato rispetto a ciascuno degli assi di base dello spazio.
Ad esempio, nella tupla di ridimensionamento \ ((s_ 0, s_ 1, s_ 2) \), \ (s_ 0 \) rappresenta il ridimensionamento lungo l'asse X, \ (s_ 1 \) lungo l'asse Y e \ (s_ 2 \) lungo l'asse Z..
La matrice di trasformazione di ridimensionamento è la seguente (dove \ (s_ 0 \), \ (s_ 1 \) e \ (s_ 2 \) sono gli elementi della tupla a tre dimensioni):
\ [
\ Begin bmatrix
s0 & 0 & 0 \\
0 e s1 e 0 \\
0 & 0 e s2
\ End bmatrix
\]
Per rendere il vettore di input A \ ((a_ 0, a_ 1, a_ 2) \) due volte più grande lungo l'asse X (ovvero, utilizzando una tupla a 3 scaling \ (S = ( 2, 1, 1) \)), la matematica sarebbe simile a questa:
\ [
\ Begin allineati
\ Begin bmatrix
b_ 0 \\
b_ 1 \\
b_ 2
\ End bmatrix
& =
\ Begin bmatrix
s0 & 0 & 0 \\
0 e s1 e 0 \\
0 & 0 e s2
\ End bmatrix
\ Begin bmatrix
a_ 0 \\
a_ 1 \\
a_ 2
\ End \\ bmatrix
& =
\ Begin bmatrix
2 & 0 & 0 \\
0 & 1 & 0 \\
0 e 0 e 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 allineato
\]
Quindi se viene dato il vettore di input \ (A = (3,4,0) \), allora il vettore di output \ (B \) sarebbe \ ((6,4,0) \).
Come altro esercizio, aggiungi una nuova funzione alla tua classe vettoriale per il ridimensionamento. Questa nuova funzione dovrebbe incorporare una tupla in 3 livelli e restituire un vettore di output.
Il flusso di base delle tue funzioni dovrebbe essere il seguente:
y0 = x0 * s0; y1 = x1 * s1; y2 = x2 * s2
).Ora che hai trasformazioni lineari sotto la cintura, costruiamo un breve programma per mostrare le tue nuove abilità. Creeremo un programma che disegna un gruppo di punti sullo schermo e quindi ci consente di modificarli nel loro complesso eseguendo trasformazioni lineari su di essi.
Prima di iniziare, vorremmo anche aggiungere un'altra funzione al nostro Punto
classe. Questo sarà chiamato setPointToPoint ()
, e semplicemente imposterà la posizione del punto corrente a quella del punto che gli viene passato. Riceverà un punto come input e non restituirà nulla.
Ecco alcune specifiche rapide per il nostro programma:
Le nostre classi attuali:
Point Class Variables: num tuple [3]; // (x, y, z) Operatori: Punto AddVectorToPoint (Vettore); Point SubtractVectorFromPoint (Vector); Vector SubtractPointFromPoint (Point); // imposta la posizione del punto corrente su quella del punto immesso Null SetPointToPoint (Point); Funzioni: // disegna un punto nella sua posizione tupla con il tuo drawPoint grafico API preferito; Classe vettoriale Variabili: num tuple [3]; // (x, y, z) Operatori: Vector AddVectorToVector (Vector); Vector SubtractVectorFromVector (Vector); Vector RotateXY (gradi); Vector RotateYZ (gradi); Vector RotateXZ (gradi); Scala vettoriale (s0, s1, s2);
Con quelle specifiche, diamo un'occhiata a cosa potrebbe essere il nostro codice:
main // setup per la tua API grafica preferita qui // setup per input da tastiera (potrebbe non essere necessario) qui // creare una matrice di 100 punti Point Array pointArray [100]; per (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();
Ora dovresti avere un piccolo programma per mostrare tutte le tue nuove tecniche! Puoi controllare la mia semplice demo qui.
Anche se certamente non abbiamo coperto tutte le possibili trasformazioni lineari disponibili, il nostro micromotore sta iniziando a prendere forma.
Come sempre, ci sono alcune cose che sono state lasciate fuori dal nostro motore per semplicità (in particolare tosatura e riflessi in questa parte). Se vuoi saperne di più su questi due tipi di trasformazioni lineari, puoi saperne di più su di loro su Wikipedia e sui relativi collegamenti.
Nella parte successiva di questa serie, copriremo diversi spazi di visualizzazione e come selezionare gli oggetti che sono al di fuori della nostra vista.
Se hai bisogno di ulteriore aiuto, vai su Envato Studio, dove puoi trovare moltissimi fantastici servizi di design e modellazione 3D. Questi fornitori esperti possono aiutarti con una vasta gamma di progetti diversi, quindi basta consultare i fornitori, leggere le recensioni e le valutazioni e scegliere la persona giusta per aiutarti.
Servizi di progettazione e modellazione 3D su Envato Studio