顶点
顶点是网格最基础的组成部分,可通过mesh.vertices获取和赋值。
mesh.vertices是一个Vector3的数组,每个Vector3为此顶点与此游戏物体的相对坐标(local position)。
顶点的位置,数量没有任何限制。几个同样的顶点可以组合成若干不同形状,不同数量的三角形。
// 手动设置顶点:
mesh = new Mesh ();
Vector3[] v3s=new Vector3[3];
v3s [0] = new Vector3 (0, 0, 0);
v3s [1] = new Vector3 (0, 1, 0);
v3s [2] = new Vector3 (1, 0, 0);
mesh.vertices = v3s;
三角形
三角形数组仅仅是顶点的索引数值,每个三角形包含三个索引。
mesh.triangles决定了网格中的三角形的形状和朝向。
triangles是一个int数组,此数组长度永远是3的倍数,每三个int代表的是由哪三个顶点并由什么顺序(朝向)来组成一个三角形。
// 手动设置三角形:
int[] index = new int[3];
index [0] = 0;
index [1] = 1;
index [2] = 2;
mesh.triangles = index;
等号右侧的0,1,2表明网格中将会有一个以 mesh.vertices[0],mesh.vertices[1],和mesh.vertices[2]组成的三角形。
此三角形的形状有此三个顶点的位置决定,而它的朝向则由此三个顶点的旋转方向决定。
三角形的三个点顺时针的是朝向我们,逆时针则背对我们。
任何一个三角形必须单独设定顶点组合顺序,且此顺序与其他三角形不发生关系。
mesh内的顶点可以用来组合三角形,也可不用来组合三角形,此处无绝对的对应关系。
UV
uv是为了防止与xy混淆的另一种写法。u为x轴,v为y轴,无数值限制(材质上的uv值一般是0-1,当进行采样的uv值大于1时会按取1的余数来计算)。
uv值用处很多,例如材质texture的采样,或是利用它的一些特性(范围0-1;同一个uv区域每个像素的uv值皆不同)进行GPU内的一些计算。
mesh.uv作为一个Vector2数组与mesh.vertices的长度必须一致,每个uv的顺序与vertices的顺序也是一一对应>,既uv[0]代表的是vertices[0]的uv值。而与三角形组成的顺序无关。
当每个顶点被赋予uv值后,在片段着色阶段,片段着色器将会根据顶点的uv值将此三角形覆盖的每个像素的uv值进行自动插值。
uv插值是以三角形为单位进行的,既三角形ABC内各个像素的uv值与其他周边三角形各顶点的uv值是无关的。
法线
可通过mesh.normals获取。
给网格赋值顶点并组成三角形,将此网格赋值给场景中的meshfilter,meshrenderer就可以将此三角形渲染出来了,但是法线并不一定是正确的,例如上面声明的三个顶点:
v3s [0] = new Vector3 (0, 0, 0);
v3s [1] = new Vector3 (0, 1, 0);
v3s [2] = new Vector3 (1, 0, 0);
不论是由0-1-2还是2-1-0的顺序组成三角形,此三角形的法线总是指向z轴方向,在有光照的场景中会出现错误的效果,这里unity提供了一个方法:
mesh.RecalculateNormals(); //自动将每个顶点的法线调整到正确方向,既是与面的朝向一致
mesh.RecalculateBounds(); //重新计算mesh.bounds,通过mesh.bounds可访问size,center等一些网格的属性
在将mesh赋值给meshfilter前需要先调用此两方法。
normal是影响光照效果,即在光照下的凹凸平滑程度,所以用插值平均后的Normal代替所有的顶点的各自的normal可使低精度模型看起来平滑。
// 编辑器下显示 normal 的脚本
using UnityEngine;
[ExecuteInEditMode]
public class ShowNormals : MonoBehaviour {
public float length = 1;
public Vector3 bias;
void Update() {
Mesh mesh = GetComponent<MeshFilter>().sharedMesh;
Vector3[] vertices = mesh.vertices;
Vector3[] normals = mesh.normals;
for (var i = 0; i < normals.Length; i++)
{
Vector3 pos = vertices[i];
pos.x *= transform.localScale.x;
pos.y *= transform.localScale.y;
pos.z *= transform.localScale.z;
pos += transform.position + bias;
Debug.DrawLine
(
pos,
pos + normals[i] * length, Color.red);
}
}
}
例子:双面网格的实现
可以利用上面的知识实现一个双面的网格,既是利用四个顶点以顺时针方向组成两个三角形再以逆时针方向组成两个三角形,四个顶点赋uv值后每个点的正反两面的uv皆相同,进而出现镜像效果。此效果与shader调用cull off选项后的效果相同,以下为实现方法:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Triangle : MonoBehaviour {
public MeshFilter meshFilter;
private Mesh mesh;
void Start () {
mesh = new Mesh ();
Vector3[] v3s = new Vector3[4];
v3s [0] = new Vector3 (0, 0, 0);
v3s [1] = new Vector3 (0, 1, 0);
v3s [2] = new Vector3 (1, 0, 0);
v3s [3] = new Vector3 (1, 1, 0);
mesh.vertices = v3s;
int[] index = new int[12];
index [0] = 0;
index [1] = 1;
index [2] = 2;
index [3] = 3;
index [4] = 2;
index [5] = 1;
index [6] = 1;
index [7] = 0;
index [8] = 2;
index [9] = 3;
index [10] = 1;
index [11] = 2;
mesh.triangles = index;
Vector2[] uvs = new Vector2[4];
uvs [0] = Vector2.zero;
uvs [1] = Vector2.up;
uvs [2] = Vector2.right;
uvs [3] = Vector2.one;
mesh.uv = uvs;
mesh.RecalculateBounds();
mesh.RecalculateNormals();
meshFilter.mesh = mesh;
}
}
参考文档:
Unity Shader:Unity网格(1)—顶点,三角形朝向,法线,uv,以及双面渲染三角形
作者:liu_if_else
Mesh 顶点的Normal
作者:何三林
Unity3D Mesh小课堂(一)三角形
Unity3D Mesh小课堂(二)为三角形添加纹理
Unity3D Mesh小课堂(三)圆形与圆环
Unity3D Mesh小课堂(四)MeshRenderer的material和sharedMaterial
Unity3D Mesh小课堂(五)CombineMeshes合并网格