Trước hết, đối với mỗi đỉnh 3D có các vectơ tiếp tuyến và tiếp tuyến vô hạn. Hình ảnh dưới đây giải thích tại sao có vô số không gian tiếp tuyến cho mỗi đỉnh, tiếp tuyến và bitangent có thể có bất kỳ hướng nào trong mặt phẳng hiển thị.
Vì vậy, để sắp xếp đúng không gian 1 tiếp tuyến hữu ích nhất , chúng tôi muốn không gian tiếp tuyến của chúng ta được căn chỉnh sao cho trục x (tiếp tuyến) tương ứng với hướng u trong bản đồ va chạm và trục y (bitangent) tương ứng với hướng v trong bản đồ vết sưng, chúng ta đã có bình thường của đỉnh đã tương ứng với hướng Z trong không gian tiếp tuyến.
(1) hữu ích nhất vì cuối cùng chúng ta muốn các vectơ bình thường được lấy mẫu từ kết cấu
Điều đó tốt nhất được giải thích bằng hình ảnh, chúng tôi muốn không gian tiếp tuyến của chúng tôi được căn chỉnh như (u, v)
hiển thị bên dưới.
Nguồn hình ảnh mặc dù không liên quan chặt chẽ đến đồ họa máy tính
Trong các nhà phát triển đồ họa máy tính thường sử dụng (u,v)
còn được gọi là tọa độ kết cấu. Chúng ta sẽ giả sử T là tiếp tuyến và B là bitangent và P0
là đỉnh đích của chúng ta, đó là một phần của tam giác (P0,P1,P2)
.
Trước tiên hãy nhớ những gì chúng tôi muốn làm, là tính toán tiếp tuyến và bitanget rằng:
- T thẳng hàng với u và B thẳng hàng với v.
- T và B nằm trong mặt phẳng với đỉnh bình thường (mặt phẳng thể hiện trong hình trên).
Vấn đề là chúng ta đã giả sử rằng T và B nằm trong cùng một mặt phẳng và tương ứng với U và V bây giờ nếu chúng ta có thể biết giá trị của chúng, chúng ta có thể xuyên qua sản phẩm và vectơ thứ ba để xây dựng ma trận biến đổi từ thế giới sang không gian tiếp tuyến.
Cho rằng chúng ta biết rằng bất kỳ vectơ 2D nào cũng có thể được viết dưới dạng kết hợp tuyến tính của hai vectơ độc lập 2 và vì chúng ta đã có các điểm tam giác (các cạnh), được hiển thị trong hình trên. Chúng tôi có thể viết:
E1 = (u1-u0) T + (v1-v0) B
E 2 = (u2-u0) T + (v2-v0) B
(2) thực sự đó là cách ma trận cơ sở được bắt nguồn
Phương trình trên có thể được viết dưới dạng ma trận,
| E1x E1y E1z | | deltaU1 deltaV1 | * | Tx Ty Tz |
| E2x E2y E2z | = | deltaU2 deltaV2 | | Bx By Bz |
Bằng cách giải phương trình ma trận, chúng ta có thể xác định giá trị T và B, chúng ta có thể xây dựng ma trận biến đổi.
Mã nguồn đầy đủ trong 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;
}
Mã nguồn đầy đủ và dẫn xuất có thể được tìm thấy ở đây .