手动创建Mesh及相关理论知识

创建Mesh

这篇教程说的非常清楚.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))]
public class CodeMeshTriangle : MonoBehaviour
{
public Transform p0;
public Transform p1;
public Transform p2;

private void Generate()
{
GetComponent<MeshFilter>().mesh = mMesh = new Mesh();

var vertices = new[] {p0.localPosition, p1.localPosition, p2.localPosition};
var triangles = new[] {0, 1, 2};

mMesh.vertices = vertices;
mMesh.triangles = triangles;
}
}

构建一个Mesh只需要两部分,一个是顶点坐标(vertices),一个是由顶点坐标构成的三角形(triangles)

15123415221848

需要注意就是其正反面,顺时针(clockwise)正面,逆时针(Counter-clockwise)反面

UV映射及Normal映射

教程前半部分很好理解,比较让我困惑的是后半部分关于NormalMap中TBN(Tangent,Bitangent,Normal)的阐述.

NormalMap的含义

首先要说的是什么是NormalMap,官网解说的非常清楚了.

普通Mesh
QQ20171204-065816

加入NormalMap
QQ20171204-065917

each pixel in the texture of the normal map (called a texel) represents a deviation in surface normal direction away from the “true” surface normal of the flat (or smooth interpolated) polygon.

Normal映射及TBN

最让我不理解的是教程中关于切线(tangents)的解释. 他直接说到

As we have a flat surface, all tangents simply point in the same direction,which is to the right.

然后直接给出代码

1
Vector4 tangent = new Vector4(1f, 0f, 0f, -1f);

让人不明所以,不知道他这个为啥直接就point了right.

不过搞清楚这点要有几个预备知识.

首先是UV映射. 可以看Y2B上的这个视频,

QQ20171204-071033

没有涉及到太底层的数学推导,感性的认知UVMaping就是

给定UV中的一个三角形U(三个点分别为U1,U2,U3) => 通过某种方法映射到 => 模型中的三角形P(P1,P2,P3)

这个视频有开了一个头,但是也没具体讲. 不过具体的映射方法,应该是和线性插值有关,没有细研究

QQ20171203-190005

可以用线性插值,双线性插值,Trilinear,Bilinear 作为Key搜搜看.

不过重点是下面这张图

QQ20171203-182318

个人理解,不一定对. 无论UV映射也好,Normal映射也好. 都需要将贴图首先旋转到与需要映射的三角形相同的一个角度. 也就是对UV图,Normal图的坐标系有旋转

如图,左边的P’三角形 和右边的P三角形 就不在一个平面. 相应的做UV时候,其坐标轴也发生的旋转.

而这个旋转的角度 从某种角度来说就是TBN

原因可以看OpenGL的教程

选定Normal后
15123440805547

垂直与该Normal的向量有很多

15123441065379

为了方便计算,所以选择和和纹理空间对齐方向
15123441425223

结合我自己画的那个图,也就是说 纹理空间的方向就是P’三角形所在的那个坐标系. 这个坐标系也就是UV的坐标系,也是NormalMap的切线(Tangent)和副切线(Bitangent)方向.

至于这个坐标系的求出

给定UV中的一个三角形U(三个点分别为U1,U2,U3) => 通过某种方法映射到 => 模型中的三角形P(P1,P2,P3)

我是理解为,给出了UV上的三个点,然后给出模型上的三个点,Unity已经就帮我们算出来了.(通过数学推导肯定是可以算出来的,我没有细研究).

所以我理解的设置NormalMap流程应该是,设定好UV,然后给定Normal方向,最后通过Mesh.RecalculateTangents重新计算出TB两个方向,而不是反过来直接给定了Vector4 tangent = new Vector4(1f, 0f, 0f, -1f);

不过直接给定Tangent角度现在想想似乎也可以理解. 教程中的Mesh角度和我自己画的图中右侧的正方形一样,此时UV图面没有任何旋转,(1,0,0)也就是其x轴方向.

完整代码:

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
[RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))]
public class CodeMeshTriangle : MonoBehaviour
{
public Transform p0;
public Transform p1;
public Transform p2;

private Mesh mMesh;
private Vector3[] mVertices;
private Vector3[] mNormals;

private void Update()
{
Generate();
}

private void Generate()
{
GetComponent<MeshFilter>().mesh = mMesh = new Mesh();

var normal = new Vector3(0, 0, -1);
mVertices = new[] {p0.localPosition, p1.localPosition, p2.localPosition};
mNormals = new[] {normal, normal, normal};
var triangles = new[] {0, 1, 2};

Vector2[] uv = new Vector2[3];
uv[0] = new Vector2(0, 0);
uv[1] = new Vector2(0, 1);
uv[2] = new Vector2(1, 0);

mMesh.vertices = mVertices;
mMesh.triangles = triangles;
mMesh.uv = uv;
mMesh.normals = mNormals;

mMesh.RecalculateTangents();
}
}
坚持原创技术分享,您的支持将鼓励我继续创作!