En primer lugar, para cada vértice 3D hay infinitos vectores tangentes y bi-tangentes. La imagen a continuación explica por qué hay un número infinito de espacios tangentes para cada vértice, la tangente y la bitangente pueden tener cualquier dirección en el plano mostrado.
Entonces, para calcular adecuadamente el espacio tangente 1 más útil , queremos que nuestro espacio tangente esté alineado de modo que el eje x (la tangente) corresponda a la dirección u en el mapa de relieve y el eje y (bitangente) corresponda a la dirección v en el mapa de relieve, ya deberíamos tener la normalidad del vértice que ya corresponde a la dirección Z en el espacio tangente.
(1) más útil porque al final queremos que los vectores normales se muestreen a partir de la textura
Eso se explica mejor con imágenes, queremos que nuestro espacio tangente se alinee como se (u, v)
muestra a continuación.
La fuente de la imagen, aunque no está estrictamente relacionada con los gráficos por computadora
En los gráficos de computadora, los desarrolladores suelen usar (u,v)
también conocidos como coordenadas de textura. Asumiremos que T es la tangente y B es la bitangente, y P0
es nuestro vértice objetivo, que es parte del triángulo (P0,P1,P2)
.
Primero recuerde lo que queríamos hacer, es calcular tangente y bitanget que:
- T alineado con u y B alineado con v.
- T y B se encuentran en el plano con el vértice normal (el plano que se muestra en la imagen de arriba).
El punto es que ya asumimos que T y B se encuentran en el mismo plano y corresponden a U y V ahora si podemos conocer sus valores podemos cruzar el producto y el tercer vector para construir una matriz de transformación del mundo al espacio tangente.
Dado que sabemos que cualquier vector 2D puede escribirse como una combinación lineal de dos vectores independientes 2 y dado que ya tenemos los puntos triangulares (bordes), que se muestran en la imagen de arriba. Podemos escribir:
E1 = (u1-u0) T + (v1-v0) B
E2 = (u2-u0) T + (v2-v0) B
(2) en realidad así es como se deriva la matriz base
La ecuación anterior se puede escribir en forma de matriz,
| E1x E1y E1z | | deltaU1 deltaV1 | * | Tx Ty Tz |
| E2x E2y E2z | = | deltaU2 deltaV2 | | Bx By Bz |
Al resolver la ecuación de matrices podemos determinar los valores de T y B, podemos construir una matriz de transformación.
El código fuente completo en 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;
}
El código fuente completo y la derivación se pueden encontrar aquí .