基本概念
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)时,就会重复贴上四张图片
如图:
示例
先画一个最简单的三角形面
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));
}
}
}
顶点和顶点索引如下图所示
上面的示例绘制的是三角形,只有一个面,只需要三个顶点数据就够了,如果绘制一个矩形,则是两个面,需要几个顶点?如果是一个立方体呢,需要几个顶点