임의의 Mesh에 대한 탄젠트 공간 벡터 계산하기
http://www.terathon.com/code/tangent.html
Modern bump mapping (also known as normal mapping) requires that tangent plane basis vectors be calculated for each vertex in a mesh. This article presents the theory behind the computation of per-vertex tangent spaces for an arbitrary triangle mesh and provides source code that implements the proper mathematics.
Mathematical Derivation
[This derivation also appears in Mathematics for 3D Game Programming and Computer Graphics, 3rd ed., Section 7.8 (or in Section 6.8 of thesecond edition).]
We want our tangent space to be aligned such that the x axis corresponds to theu direction in the bump map and they axis corresponds to thev direction in the bump map. That is, ifQ represents a point inside the triangle, we would like to be able to write
Q −
P
0 = (
u −
u
0)
T + (
v −
v
0)
B,
where T and B are tangent vectors aligned to the texture map,P0 is the position of one of the vertices of the triangle, and (u0, v0) are the texture coordinates at that vertex. The letterB stands for bitangent, but in many places it is stilled calledbinormal because of a mix-up in terms when tangent-space bump mapping first became widespread. (See “Bitangent versus Binormal” below.)
Suppose that we have a triangle whose vertex positions are given by the pointsP0,P1, andP2, and whose corresponding texture coordinates are given by (u0, v0), (u1, v1), and (u2, v2). Our calculations can be made much simpler by working relative to the vertexP0, so we let
Q
1 =
P
1 −
P
0
Q
2 =
P
2 −
P
0
and
(
s
1,
t
1) = (
u
1 −
u
0,
v
1 −
v
0)
(
s
2,
t
2) = (
u
2 −
u
0,
v
2 −
v
0).
We need to solve the following equations for T and B.
Q
1 =
s
1
T +
t
1
B Q
2 =
s
2
T +
t
2
B
This is a linear system with six unknowns (three for each T andB) and six equations (thex,y, andz components of the two vector equations). We can write this in matrix form as follows.
[
(Q1)x (Q2)x
(Q1)y (Q2)y
(Q1)z (Q2)z
]
=
[
s1 s2
t1 t2
]
[
Tx Bx
Ty By
Tz Bz
]
Multiplying both sides by the inverse of the (s, t) matrix, we have
[
Tx Bx
Ty By
Tz Bz
]
=
1 s1t2 −s2t1
[
t2 −s2
−t1 s1
]
[
(Q1)x (Q2)x
(Q1)y (Q2)y
(Q1)z (Q2)z
]
.
This gives us the (unnormalized) T and B tangent vectors for the triangle whose vertices areP0,P1, andP2. To find the tangent vectors for a single vertex, we average the tangents for all triangles sharing that vertex in a manner similar to the way in which vertex normals are commonly calculated. In the case that neighboring triangles have discontinuous texture mapping, vertices along the border are generally already duplicated since they have different mapping coordinates anyway. We do not average tangents from such triangles because the result would not accurately represent the orientation of the bump map for either triangle.
Once we have the normal vector N and the tangent vectors T and B for a vertex, we can transform from tangent space into object space using the matrix
[
Tx Ty Tz
Bx By Bz
Nx Ny Nz
].
To transform in the opposite direction (from object space to tangent space—what we want to do to the light direction), we can simply use the inverse of this matrix. It is not necessarily true that the tangent vectors are perpendicular to each other or to the normal vector, so the inverse of this matrix is not generally equal to its transpose. It is safe to assume, however, that the three vectors will at least be close to orthogonal, so using the Gram-Schmidt algorithm to orthogonalize them should not cause any unacceptable distortions. Using this process, new (still unnormalized) tangent vectors
T′ and
B′ are given by
T′ =
T − (
N ·
T)
N B′ =
B − (
N ·
B)
N − (
T′ ·
B)
T′/
T′
2
Normalizing these vectors and storing them as the tangent and bitangent for a vertex lets us use the matrix
[
T′x B′x Nx
T′y B′y Ny
T′z B′z Nz
]
(*)
to transform the direction to light from object space into tangent space. Taking the dot product of the transformed light direction with a sample from the bump map then produces the correct Lambertian diffuse lighting value.
It is not necessary to store an extra array containing the per-vertex bitangent since the cross productN × T′ can be used to obtainmB′, wherem = ±1 represents the handedness of the tangent space. The handedness value must be stored per-vertex since the bitangentB′ obtained fromN × T′ may point in the wrong direction. The value ofm is equal to the determinant of the matrix in Equation (*). One may find it convenient to store the per-vertex tangent vectorT′ as a four-dimensional entity whosew coordinate holds the value ofm. Then the bitangentB′ can be computed using the formula
B′ =
T′w(
N ×
T′),
where the cross product ignores the w coordinate. This works nicely for vertex programs by avoiding the need to specify an additional array containing the per-vertexm values.
Bitangent versus Binormal
The term binormal is commonly used as the name of the second tangent direction (that is perpendicular to the surface normal andu-aligned tangent direction). This is a misnomer. The term binormal pops up in the study ofcurves and completes what is known as a Frenet frame about a particular point on a curve. Curves have a single tangent direction and two orthogonal normal directions, hence the terms normal and binormal. When discussing a coordinate frame at a point on asurface, there is one normal direction and two tangent directions, which should be called the tangent andbitangent.
Source Code
The code below generates a four-component tangent T in which the handedness of the local coordinate system is stored as ±1 in thew-coordinate. The bitangent vectorB is then given byB = (N × T) · Tw.
#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;
}
How to cite this article
Lengyel, Eric. “Computing Tangent Space Basis Vectors for an Arbitrary Mesh”. Terathon Software 3D Graphics Library, 2001. http://www.terathon.com/code/tangent.html
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Vector & Matrix스칼라 : 하나의 숫자로만 이루어진 데이터 (크기만 있고 방향이 없는 물리량) 벡터 : 여러 숫자로 이루어진 데이터 레코드. 매트릭스 : 벡터가 여럿인 데이터집합 벡터의 크기는 스칼라배를 통해 표현할 수 있다. *내...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.