DirectX/Calcolo vettoriale

Indice del libro

Per vettore intendiamo un oggetto che ha un modulo ed una direzione. A differenza dei vettori usati in fisica, questi non hanno un verso e non ne hanno bisogno. Un vettore può essere usato per indicare una forza (applicata con m intensità in quella direzione), una velocità (m km/h in una data direzione), ma anche una semplice direzione: la direzione verso cui punta la telecamera, la direzione in cui guarda il personaggio, la normale di un piano o di una faccia di un dato poligono. In questo caso il modulo viene impostato ad 1 e non ha valore significativo per il vettore stesso.

Rappresentazione di vettori modifica

Il modo più semplice di rappresentare un vettore è una linea. La lunghezza della linea indica il modulo, l'orientamento è la direzione. L'orientamento può essere bloccato su due assi (x, y), in questo caso si parla di Vettori 2D (D3DXVECTOR2) o può avere anche una profondità (x, y, z), diventando quindi un Vettore 3D (D3DXVECTOR3). Esiste anche un Vettore 4D (D3DXVECTOR4). La quarta coordinata (chiamata W) non può essere rappresentata ovviamente su un foglio o uno schermo, ma viene utilizzata per alcuni calcoli.

Uguaglianza tra vettori modifica

Due vettori si dicono uguali se hanno stesso modulo e stessa direzione, senza alcun influenza del punto di applicazione. Due vettori lunghi 1 m e paralleli su tutti gli assi, per quanto distanti essi siano sono uguali.

Rappresentazione informatica dei vettori modifica

Siccome i computer, come abbiamo già detto non possono lavorare geometricamente, dovremo utilizzare solo numeri per descrivere un vettore. Per il modulo nessun problema, ma per la direzione? Servirebbero 6 coordinate: 3 per la coda e 3 per la testa, da questi due punti si potrebbe ottenere la direzione. Tuttavia utilizzare 7 valori (6 coordinate + 1 modulo) per ogni vettore è altamente inefficiente, richiedendo molta memoria. Per comodità quindi si fa coincidere la coda del vettore sempre con l'origine degli assi. In questo modo utilizzando solo 3 coordinate per la testa (x, y, z), avremo già direzione e modulo. Infatti il modulo si può calcolare semplicemente come:   per il teorema di pitagora applicato allo spazio. L'origine degli assi coincide con il centro dello schermo. Mentre l'asse X ha il suo semiasse positivo a destra e l'asse Y in alto come siamo abituati, l'asse Z che perfora lo schermo può essere orientato in due modi: i valori negativi di Z che vanno dai nostri occhi all'origine e i valori positivi dall'origine al muro dietro lo schermo (sistema Left-Handed, LH) oppure al contrario, i valori negativi dal muro al monitor e positivi dal monitor agli occhi (Right-Handed, RH). Personalmente preferisco il sistema LH, DirectX tuttavia lascia la possibilità di scelta utilizzando opportune funzioni. Quando però si creano vettori, viene usato sempre il sistema LH.

Modulo di un vettore modifica

Il modulo di un vettore v si indica con il simbolo   e si calcola con il teorema di pitagora per lo spazio utilizzando come x, y, z le componenti del vettore.

Vettore normalizzato modifica

Ci sono situazioni in cui il modulo di un vettore non importa, nel caso in cui debba solo indicare una direzione. In questo caso il modulo viene impostato ad 1, ed il vettore viene detto normalizzato. Normalizzare un vettore è l'operazione di portare il modulo ad 1 senza alterarne la direzione. Per normalizzare un vettore è sufficiente dividere ogni sua componente per il modulo del vettore stesso ossia:

 

Operazioni con i vettori modifica

Uguaglianza modifica

Due vettori sono uguali se hanno le corrispettive coordinate uguali: dati a = (Ax, Ay, Az) e b = (Bx, By, Bz), a = b se e solo se Ax = Bx e Ay = By e Az = Bz. Oppure se preferite:  

Somma modifica

La somma di due vettori è un terzo vettore le cui componenti sono la somma delle componenti dei vettori originali: dati   e  ,  

Moltiplicazione con scalari modifica

Un vettore può essere moltiplicato per uno scalare k. Il risultato sarà un vettore in cui ogni componente è stata moltiplicata per lo scalare: Dato  

Differenza modifica

La differenza viene definita come combinazione di somma e moltiplicazione scalare: dati a e b vettori,  . Quindi:  

Prodotto scalare modifica

Il prodotto scalare (dot product) tra due vettori è uno scalare composto dalla somma dei prodotti delle coppie di componenti. In altre parole: dati   e   il prodotto scalare è:

 

Inoltre il prodotto scalare può essere calcolato anche come:

 

dove   è l'angolo formatosi avvicinando i vettori fino a far coincidere le code.

Prodotto vettoriale modifica

Il prodotto vettoriale (cross product) è un secondo tipo di prodotto tra vettori. Questo prodotto può essere calcolato solo tra vettori 3D, inutile quindi cercare di calcolare il prodotto vettoriale di due vettori nel piano. Il risultato di questa operazione è un terzo vettore di direzione perpendicolare al piano su cui poggiano entrambi i vettori di partenza. Le sue componenti sono:

 

Da notare che il prodotto vettoriale non gode della proprietà commutativa.

Lavorare con i vettori in DirectX modifica

Un vettore generico a 3 dimensioni è definito dalla libreria Direct3D come:

struct D3DVECTOR
{
    float x, y, z;
};

Quindi per sommare due vettori il codice sarebbe:

D3DVECTOR a, b;
// Riempiamo i vettori con dei dati...
// ...

D3DVECTOR somma;
somma.x = a.x + b.x;
somma.y = a.y + b.y;
somma.z = a.z + b.z;

Tuttavia la libreria Direct3DX viene in nostro aiuto con una fantastica classe:

class D3DXVECTOR3 : public D3DVECTOR
{
public:
    D3DXVECTOR3();
    // [...]
};

I costruttori di D3DXVECTOR3 permettono la creazione di un vettore nullo, un vettore copiato da un altro vettore e di un vettore date le tre coordinate. La grande comodità di questa classe è di supportare somma, differenza, moltiplicazione e divisione con scalari, oltre al confronto. Gestire i vettori diventa ora molto più semplice:

D3DXVECTOR3 v1();     // x = y = z = 0
D3DXVECTOR3 v2(3.0);  // x = y = z = 3.0
D3DXVECTOR3 v3(3.0, 1.5, -4.23);
D3DXVECTOR3 v4(v2);   // Copia di v2

v3 += v2;
v2 = v1 + v3;  // Somma vettoriale
v4 = v2 * 5;   // Prodotto con costante

Allo stesso modo esistono D3DXVECTOR2 e D3DXVECTOR4 (poi capiremo a cosa serve).

Lunghezza del vettore modifica

Per leggere la lunghezza, o il modulo, effettiva del vettore (e non le sue componenti) D3DX mette a disposizione queste funzioni:

float D3DXVec2Length(D3DXVECTOR2 *pV);
float D3DXVec3Length(D3DXVECTOR3 *pV);
float D3DXVec4Length(D3DXVECTOR4 *pV);

Normalizzazione di vettori modifica

Abbiamo visto che normalizzare un vettore vuol dire dividere ogni componente per il modulo del vettore stesso al fine di ottenere un modulo pari ad 1. Le classi D3DXVECTOR# non contengono funzioni come Normalize, ma D3DX ci fornisce una funzione per ottenere lo stesso effetto. Queste funzioni vogliono un puntatore dove mettere il risultato che poi verrà anche restituito dalla funzione stessa. Quindi questa funzione non crea un nuovo vettore ma ne richiede due: l'originale da normalizzare e la destinazione in cui riportare il risultato. E' anche possibile far coincidere pOut e pIn, perdendo però il vettore originale.

D3DXVECTOR2 *D3DXVec2Normalize(D3DXVECTOR2 *pOut, D3DXVECTOR2 *pIn);
D3DXVECTOR3 *D3DXVec3Normalize(D3DXVECTOR3 *pOut, D3DXVECTOR3 *pIn);
D3DXVECTOR4 *D3DXVec4Normalize(D3DXVECTOR4 *pOut, D3DXVECTOR4 *pIn);

D3DXVECTOR3 a(3.4, 2.6, 0.3), aNormalized;
D3DXVec3Normalize(&aNormalized, &a);

D3DXVECTOR2 b(-4.5, 0.99);
D3DXVec2Normalize(&b, &b);

Prodotto scalare modifica

Anche per il prodotto scalare la classe D3DXVECTOR# non presenta funzioni utili. Tra l'altro la funzione operator* è utilizzata per il prodotto con scalare. Quindi il seguente codice è errato:

D3DXVECTOR3 a(5.0), b(3.2);
D3DXVECTOR3 res = a * b;    // L'operatore * moltiplica solo vettori con scalari.

Per il prodotto scalare (Dot product) D3DX fornisce i seguenti metodi:

float D3DXVec2Dot(D3DXVECTOR2 *pV1, D3DXVECTOR2 *pV2);
float D3DXVec3Dot(D3DXVECTOR3 *pV1, D3DXVECTOR3 *pV2);
float D3DXVec4Dot(D3DXVECTOR4 *pV1, D3DXVECTOR4 *pV2);

Tutte le versioni prendono come argomenti due puntatori a D3DXVECTOR#, di cui verrà calcolato il prodotto scalare (che restituisce un valore scalare, ricordiamolo), il cui risultato sarà restituito dalla funzione.

Prodotto vettoriale modifica

Infine ci sono funzioni anche per il calcolo del prodotto vettoriale (Cross product). Essendo il prodotto vettoriale non definito per un vettore 2D non esiste un D3DXVec2Cross, ma solo D3DXVec3Cross e D3DXVec4Cross.

D3DXVECTOR3 *D3DXVec3Cross(D3DXVECTOR3 *pOut, D3DXVECTOR3 *pV1, D3DXVECTOR3 *pV2);
D3DXVECTOR4 *D3DXVec4Cross(D3DXVECTOR4 *pOut, D3DXVECTOR4 *pV1, D3DXVECTOR4 *pV2);

In questo caso oltre ai due vettori di partenza viene passato come argomento anche un puntatore a vettore dove verrà inserito il risultato dell'operazione. Il valore di pOut viene anche restituito dalla funzione stessa.