Prima di tutto, per ogni vertice 3D vi sono infiniti vettori tangenti e bi-tangenti. L'immagine seguente spiega perché esiste un numero infinito di spazi tangenti per ciascun vertice, la tangente e il bitangente possono avere qualsiasi direzione nel piano mostrato.
Quindi, per calcolare correttamente il più utile 1 spazio tangente , vogliamo che il nostro spazio tangente sia allineato in modo tale che l'asse x (la tangente) corrisponda alla direzione u nella mappa di rilievo e l'asse y (bitangente) corrisponda alla direzione v nella bump map, dovremmo già avere un normale del vertice che corrisponde già alla direzione Z nello spazio tangente.
(1) più utile perché alla fine vogliamo che i vettori normali vengano campionati dalla trama
Che sia meglio spiegato con le immagini, vogliamo che il nostro spazio tangente sia allineato come (u, v)
mostrato di seguito.
Fonte dell'immagine sebbene non strettamente correlata alla computer grafica
In genere gli sviluppatori di grafica per computer usano (u,v)
anche le coordinate di trama. Supponiamo che T sia la tangente e B sia il bitangente, ed P0
è il nostro vertice bersaglio, che fa parte del triangolo(P0,P1,P2)
.
Per prima cosa ricorda cosa volevamo fare, è calcolare tangente e bitanget che:
- T allineato con ue B allineato con v.
- T e B si trovano sul piano con il vertice normale (il piano mostrato nell'immagine sopra).
Il punto è che abbiamo già ipotizzato che T e B giacciono sullo stesso piano e corrispondano a U e V ora se possiamo conoscere i loro valori possiamo incrociare il prodotto e il terzo vettore per costruire una matrice di trasformazione dal mondo allo spazio tangente.
Dato che sappiamo che qualsiasi vettore 2D può essere scritto come una combinazione lineare di due vettori indipendenti 2 e poiché abbiamo già i punti del triangolo (bordi), mostrati nell'immagine sopra. Possiamo scrivere:
E1 = (u1-u0) T + (v1-v0) B
E2 = (u2-u0) T + (v2-v0) B
(2) in realtà è così che viene derivata la matrice di base
L'equazione di cui sopra può essere scritta in forma di matrice,
| E1x E1y E1z | | deltaU1 deltaV1 | * | Tx Ty Tz |
| E2x E2y E2z | = | deltaU2 deltaV2 | | Bx By Bz |
Risolvendo l'equazione delle matrici possiamo determinare i valori T e B e possiamo costruire una matrice di trasformazione.
Il codice sorgente completo in C ++
#include "Vector4D.h"
struct Triangle
{
unsigned short index[3];
};
void CalculateTangentArray(long vertexCount, const Point3D *vertex, const Vector3D *normal,
const Point2D *texcoord, long triangleCount, const Triangle *triangle, Vector4D *tangent)
{
Vector3D *tan1 = new Vector3D[vertexCount * 2];
Vector3D *tan2 = tan1 + vertexCount;
ZeroMemory(tan1, vertexCount * sizeof(Vector3D) * 2);
for (long a = 0; a < triangleCount; a++)
{
long i1 = triangle->index[0];
long i2 = triangle->index[1];
long i3 = triangle->index[2];
const Point3D& v1 = vertex[i1];
const Point3D& v2 = vertex[i2];
const Point3D& v3 = vertex[i3];
const Point2D& w1 = texcoord[i1];
const Point2D& w2 = texcoord[i2];
const Point2D& w3 = texcoord[i3];
float x1 = v2.x - v1.x;
float x2 = v3.x - v1.x;
float y1 = v2.y - v1.y;
float y2 = v3.y - v1.y;
float z1 = v2.z - v1.z;
float z2 = v3.z - v1.z;
float s1 = w2.x - w1.x;
float s2 = w3.x - w1.x;
float t1 = w2.y - w1.y;
float t2 = w3.y - w1.y;
float r = 1.0F / (s1 * t2 - s2 * t1);
Vector3D sdir((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r,
(t2 * z1 - t1 * z2) * r);
Vector3D tdir((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r,
(s1 * z2 - s2 * z1) * r);
tan1[i1] += sdir;
tan1[i2] += sdir;
tan1[i3] += sdir;
tan2[i1] += tdir;
tan2[i2] += tdir;
tan2[i3] += tdir;
triangle++;
}
for (long a = 0; a < vertexCount; a++)
{
const Vector3D& n = normal[a];
const Vector3D& t = tan1[a];
// Gram-Schmidt orthogonalize
tangent[a] = (t - n * Dot(n, t)).Normalize();
// Calculate handedness
tangent[a].w = (Dot(Cross(n, t), tan2[a]) < 0.0F) ? -1.0F : 1.0F;
}
delete[] tan1;
}
Il codice sorgente completo e la derivazione sono disponibili qui .