1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
|
// 显示模型的normal, tangnent, binormal
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ModelTBNShow : MonoBehaviour
{
[Range(0f, 10f)]
public float tbnLen = 0.1f;
[Range(0, 1000)]
public int maxShowNum = 100;
public bool showNormal = true;
public bool showTangent = true;
public bool showBiTangent = true;
MeshFilter meshFilter;
Mesh sharedMesh;
Matrix4x4 localToWorld;
Matrix4x4 localToWorldInverseTranspose;
private void OnDrawGizmos()
{
meshFilter = GetComponent<MeshFilter>();
sharedMesh = meshFilter.sharedMesh;
localToWorld = meshFilter.transform.localToWorldMatrix;
localToWorldInverseTranspose = localToWorld.inverse.transpose;
Vector3[] vertices = sharedMesh.vertices;
Vector3[] normals = sharedMesh.normals;
Vector4[] tangents = sharedMesh.tangents;
int tangentsLen = (tangents != null ? tangents.Length : 0);
Vector3[] biTangents = new Vector3[tangentsLen];
Vector3[] tangentsData = new Vector3[tangentsLen];
for (int i = 0; i < tangentsLen; i++)
{
//切向量数据 Vector4 转 Vector3
tangentsData[i].x = tangents[i].x;
tangentsData[i].y = tangents[i].y;
tangentsData[i].z = tangents[i].z;
//计算副切线 cross(法向量,切向量)*坐标系方向参数
biTangents[i] = Vector3.Cross(normals[i], tangentsData[i]) * tangents[i].w;
}
/*
* localToWorld 将 顶点位置 从模型坐标系转到世界坐标系矩阵
* localToWorldInverseTranspose 将 向量 从模型坐标系转到世界坐标系矩阵
* 1、切向量t和副切向量b 由于方向与纹理坐标系一致 使用localToWorld和localToWorldInverseTranspose矩阵转换到世界坐标系 结果相同
* 2、normal 由于模型有非等比缩放的情况,缩放后顶点的法向量使用localToWorld矩阵转换的结果不正确
* 设矩阵M为切向量t的转换矩阵,矩阵G为法向量n的转换矩阵,
* 转换后的切向量且t2 = M*t, 转换后的法向量n2 = G*n,同时要求 n2 * t2 = 0
* 所以 (G*n)' * (M*t) = 0 => n'*G'*M*t = 0 (n'表示向量n的转置, G'表示矩阵G的转置)
* 已知 n'*t = 0(法向量和切向量垂直), 此时如果令 G'*M = I(单位矩阵)
* 则有 n'*G'*M*t = n'*I*t = n'*t = 0 成立
* 可得 G'*M = I => G = (inverse(M))'
*/
if (showNormal) DrawVectors(vertices, normals, ref localToWorld, ref localToWorldInverseTranspose, Color.red, tbnLen);
if (showTangent) DrawVectors(vertices, tangentsData, ref localToWorld, ref localToWorld, Color.green, tbnLen);
if (showBiTangent) DrawVectors(vertices, biTangents, ref localToWorld, ref localToWorld, Color.blue, tbnLen);
}
/*显示向量
* vertexs 向量初始位置
* vectors 向量方向
* vertexMatrix 向量初始位置从模型坐标系转到世界坐标系矩阵
* vectorMatrix 向量方向从模型坐标系转到世界坐标系矩阵
* color 向量颜色
* */
void DrawVectors(Vector3[] vertexs, Vector3[] vectors, ref Matrix4x4 vertexMatrix, ref Matrix4x4 vectorMatrix, Color color, float vectorLen)
{
Gizmos.color = color;
int len = (vertexs == null || vectors == null ? 0 : vertexs.Length);
len = Mathf.Min(len, maxShowNum);
if (vertexs.Length != vectors.Length)
{
Debug.LogError("vertexs lenght not equal vectors length!!!");
return;
}
for (int i = 0; i < len; i++)
{
Vector3 vertexData = vertexMatrix.MultiplyPoint(vertexs[i]);
Vector3 vectorData = vectorMatrix.MultiplyVector(vectors[i]);
vectorData.Normalize();
Gizmos.DrawLine(vertexData, vertexData + vectorData * vectorLen);
}
}
}
|