Unity

Unity 中网格是如何绘制的(一)

2022-08-30 #Unity#渲染

本文迁移自旧站,保留当时的学习记录;涉及版本与实践结论的内容会在后续复查中逐步更新。

基本概念

Mesh

网格 由N个三角形面组成

三角形 组成网格的基本单元,三角形是由三个顶点按特定顺序(顺时针、逆时针)组成

顶点 是组成三角形的基本单元,众所周知,最少三个点才能确定一个面

顶点索引 决定组成三角形的顶点和顺序,在Unity中顶点按顺时针连接为正面,逆时针为反面,遵循 左手法则

法线 垂直与三角面的向量,方向由顶点索引的顺序根据左手法则确定

UV坐标 贴图左下角为(0,0)右上角为(1,1),UV坐标和顶点坐标一一对应,确定三角面由贴图的哪部分渲染

切线 垂直于法线的向量,但是这样的向量有无数条,最终由UV中的U(水平方向)增长方向确定

还有其他数据,不一一列举

Mesh 就是这些信息的集合

总结一下:顶点数据提供了创建Mesh所需要的所有顶点坐标,顶点索引提供了哪三个顶点以顺时针还是逆时针来组成三角形,UV坐标提供了顶点在贴图的哪个位置开始取样

Vertices 顶点数组 Vector3[]

它存储的是顶点的相关信息,所谓点成线,线成面,可以理解为这里面存储的是构成网格面全部的点

Topology 拓扑类型

它存储的就是一个类型信息,可以理解为它是图形表面排列结构的组成方式,Unity给我们提供了5种拓扑类型,三角面、四边形、线条、虚线、点阵,最常用的则是三角面

Indices 索引数组 int[]

它是每个三角面顶点的索引,可以理解为他存储了构成网格三角面所用到的顶点数组的索引。

Normal 法线 Vector3[]

法线就是垂直于该顶点三角面的一条三维向量,它只有方向,没有大小。法线的方向就是顶点三角面朝外的方向。假设我们面前有一面镜子,它的正中心会有一条法线垂直于镜面指向我们,指向我们的面就是正面,相反就是背面,(由索引顺序根据左手法则确定)

Tangent 切线 Vector3[]

它是垂直于法线的一条向量,而由于垂直于法线的向量有无数条,所以切线最终是由UV坐标来决定朝向的

UV纹理坐标 Vector2[]

上面所说的UV坐标其实就是它,U增长的方向就是切线的方向,它和三维空间的X, Y, Z较为类似,它是一个二维的坐标系统,模型网格除了有三维空间的xyz坐标外,还有一个二维的UV坐标,在UV坐标中,U和V分别代表顶点在Texture水平和垂直方向上的采样坐标,这些坐标通常位于(0,0)和(1,1)之间,(0,0)代表最左下角,而(1,1)代表最右上角

这就跟平时装修房子贴墙纸一样,可以理解为它是Texture映射到模型表面的依据,模型顶点会依据UV坐标对Texture进行采样。比如,定点的UV坐标为(0,0),那么这个顶点就会从贴图的左下角开始取样

那可不可以大于1或者小于0呢,可以,但是一般没必要 大于1或者小于0时,贴图会重复平铺,比如uv=(2,2)时,就会重复贴上四张图片

如图: uv超出范围

示例

先画一个最简单的三角形面

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
using UnityEngine;

[ExecuteAlways]
public class MeshGenerate : MonoBehaviour
{
//顶点数据
[SerializeField]
private Vector3[] vertices;
//三角形索引
[SerializeField]
private int[] triangles;
//uv坐标数据
[SerializeField]
private Vector2[] uvs;

void Start()
{
//画三角形需要三个顶点,定义三个顶点坐标,这里的坐标是相对于物体的坐标,也就是LocalPosirion
vertices = new Vector3[]{
// 顶点1
new Vector3(-2.0f, 5.0f, -2.0f),//[0]
// 顶点2
new Vector3(-2.0f, 0.0f, -2.0f),//[1]
// 顶点3
new Vector3(2.0f, 0.0f, -2.0f),//[2]
};

//定义顶点顺序,因为要绘制正面,所以按顺时针排序,记得是遵循左手坐法则,不理解左手法则的一定要理解
triangles =new int[]{
2,1,0,
};

uvs = new Vector2[]{
// 顶点1的uv,对应上面的vertices[0]
new Vector2(0.5f, 1.0f),
// 顶点2的uv,对应上面的vertices[1]
new Vector2(0.0f, 0.0f),
// 顶点3的uv,对应上面的vertices[2]
new Vector2(1.0f, 0.0f),
};

Generate();
}

private void OnValidate()
{
Generate();
}
void Generate()
{
// 新建一个Mesh
Mesh mesh = new Mesh();
// 用构建的数据初始化Mesh
mesh.vertices = vertices;
mesh.triangles = triangles;
mesh.uv = uvs;
// 法线是根据顶点数据计算出来的,所以在修改完顶点后,需要更新一下法线
mesh.RecalculateNormals();
mesh.RecalculateTangents();
// 将构建好的Mesh替换上,节点上需要挂载MeshFilter组件和MeshRenderer组件
gameObject.GetComponent<MeshFilter>().mesh = mesh;
}

private void OnDrawGizmos()
{
//这里是把法线和切线在Scene窗口上绘制出来
var mesh = gameObject.GetComponent<MeshFilter>().mesh;
if(mesh == null)
{
return;
}
var normals = mesh.normals;
var vers = mesh.vertices;
var tan = mesh.tangents;
for (int i = 0; i < vers.Length; i++)
{
Gizmos.color = Color.blue;
Gizmos.DrawLine(vers[i], vers[i] + normals[i]);
Gizmos.color = Color.green;
Gizmos.DrawLine(vers[i], vers[i] + new Vector3(tan[i].x, tan[i].y, tan[i].z));
}
}
}

顶点和顶点索引如下图所示

顶点

上面的示例绘制的是三角形,只有一个面,只需要三个顶点数据就够了,如果绘制一个矩形,则是两个面,需要几个顶点?如果是一个立方体呢,需要几个顶点

评论
分享