任意网格顶点的切线空间基计算
2012-08-25 15:47
337 查看
本文出处:
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.
second edition).]
We want our tangent space to be aligned such that the x axis corresponds to the
u direction in the bump map and the y axis corresponds to the
v direction in the bump map. That is, if Q represents a point inside the triangle, we would like to be able to write
Q − P0 = (u −
u0)T + (v − v0)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 letter
B stands for bitangent, but in many places it is stilled called
binormal 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 points
P0, P1, and P2, 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 vertex
P0, so we let
Q1 = P1 −
P0
Q2 = P2 − P0
and
(s1, t1) = (u1 −
u0, v1 − v0)
(s2, t2) = (u2 −
u0, v2 − v0).
We need to solve the following equations for T and B.
Q1 = s1T +
t1B
Q2 = s2T + t2B
This is a linear system with six unknowns (three for each T and
B) and six equations (the x, y, and z components of the two vector equations). We can write this in matrix form as follows.
Multiplying both sides by the inverse of the (s, t) matrix, we have
This gives us the (unnormalized) T and B tangent vectors for the triangle whose vertices are
P0, P1, and P2. 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
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
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 product
N × T′ can be used to obtain mB′, where
m = ±1 represents the handedness of the tangent space. The handedness value must be stored per-vertex since the bitangent
B′ obtained from N × T′ may point in the wrong direction. The value of
m is equal to the determinant of the matrix in Equation (*). One may find it convenient to store the per-vertex tangent vector
T′ as a four-dimensional entity whose w coordinate holds the value of
m. Then the bitangent B′ 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-vertex
m values.
u-aligned tangent direction). This is a misnomer. The term binormal pops up in the study of
curves 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 a surface, there is one normal direction and two tangent directions, which should be called the tangent and
bitangent.
w-coordinate. The bitangent vector B is then given by
B = (N × T) · Tw.
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 the
u direction in the bump map and the y axis corresponds to the
v direction in the bump map. That is, if Q represents a point inside the triangle, we would like to be able to write
Q − P0 = (u −
u0)T + (v − v0)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 letter
B stands for bitangent, but in many places it is stilled called
binormal 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 points
P0, P1, and P2, 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 vertex
P0, so we let
Q1 = P1 −
P0
Q2 = P2 − P0
and
(s1, t1) = (u1 −
u0, v1 − v0)
(s2, t2) = (u2 −
u0, v2 − v0).
We need to solve the following equations for T and B.
Q1 = s1T +
t1B
Q2 = s2T + t2B
This is a linear system with six unknowns (three for each T and
B) and six equations (the x, y, and z 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 |
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 | . |
P0, P1, and P2. 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 | . |
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 | (*) |
It is not necessary to store an extra array containing the per-vertex bitangent since the cross product
N × T′ can be used to obtain mB′, where
m = ±1 represents the handedness of the tangent space. The handedness value must be stored per-vertex since the bitangent
B′ obtained from N × T′ may point in the wrong direction. The value of
m is equal to the determinant of the matrix in Equation (*). One may find it convenient to store the per-vertex tangent vector
T′ as a four-dimensional entity whose w coordinate holds the value of
m. Then the bitangent B′ 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-vertex
m 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 of
curves 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 a surface, there is one normal direction and two tangent directions, which should be called the tangent and
bitangent.
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 vector B is then given by
B = (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; }
相关文章推荐
- 为任意网格计算切空间坐标系向量
- 在unity向量空间内绘制几何(3):通过三角形重心坐标计算任意形状网格上的随机坐标点
- 任意多边形顶点排序和凸包计算
- 已知空间三角形的三个顶点,计算其归一化的法向量
- Shader自学笔记 3.2.1 用切线空间计算法线纹理
- 计算三角网格的顶点法向量
- 网格顶点方向的计算(一种新的面积加权方法)
- 三维场景计算任意两点的空间距离
- 根据模型的顶点位置坐标和纹理坐标计算顶点的法线、切线和副法线
- 计算三角网格的顶点法向量
- Use Partial Derivatives To Calculate Triangle Vertex Tangent (利用偏导数计算三角形的顶点切线)
- 切线空间计算
- (Warshall13.1.1)POJ 2253(计算任意一对顶点之间的连通性||求最大路的最小值——图的传递闭包)
- 计算Mesh中顶点切线的方法
- 根据模型的顶点位置坐标和纹理坐标计算顶点的法线、切线和副法线
- 处理顶点——通过切线空间的凹凸映射添加逐像素细节
- (转)计算切线空间
- 网格顶点法向的计算(基于面平均方法)
- 计算三角网格的顶点法向量
- Unity shader学习之切线空间下计算凹凸映射