目录
一、概述
Unity 的 Mesh 是用于表示三维物体的网格数据结构。它是由一系列顶点和三角形组成的网格,用于描述物体的形状和外观。
Mesh 是由顶点、三角形和其他相关信息组成的,它用于在 Unity 中创建和渲染三维对象。顶点是网格的基本构建单元,它们定义了物体的形状,每个顶点都有三维坐标和其他可选属性,如法线、 UV 坐标和颜色。三角形则是由三个顶点组成的,它们定义了网格表面的平面,形成了物体的可见表面。
Mesh 类提供了许多方法来操作顶点和三角形,例如添加、删除、移动顶点和三角形,以及调整网格的大小和形状。这些操作可以在 Unity 编辑器中进行,也可以在代码中通过使用 Unity 的 API 来实现。
使用 Mesh 可以创建各种类型的三维对象,如静态物体、动态物体、碰撞检测对象等。在 Unity 中,Mesh 还支持各种纹理和光照技术,以便更好地渲染物体的外观和效果。
二、获取顶点坐标和索引
在讲解 Mesh 案例之前,这里先做个小案例来演示下 Unity 中模型是怎么渲染出来的,本节的主要作用就是展示 Mesh 中两个重要的知识点:顶点 和 顶点下标,即使看不明白也不要紧,后面会有更详细的案例。
在场景中新建一个 Quad
创建完成后,将 Shading Mode 的默认值 Shaded 改为 Shaded Wireframe
场景中效果如下:
图中可以看到方块中间有一条斜线,一般情况下,这么设置只是为了看模型的顶点,并没有其他作用,还是恢复成默认值吧。
接下来新添加一个脚本 Test1,拖到 Quad 上,代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Test1 : MonoBehaviour
{
void Start()
{
//1.获取mesh对象
Mesh mesh = GetComponent<MeshFilter>().mesh;
//2.获取mesh的顶点坐标数组
Vector3[] vertices = mesh.vertices;
for (int i = 0; i < vertices.Length; i++)
{
Debug.Log(vertices[i]);
}
//3.获取mesh的索引数组
int[] triangles = mesh.triangles;
for (int i = 0; i < triangles.Length; i++)
{
Debug.Log(triangles[i]);
}
}
}
运行后的打印:
注意这里的坐标是水平的
将视野转到背面可以看到,背面是透明的,根本没有渲染
由于正面和背面共用这4个顶点,只要改下顶点的索引就可以让背面同样的渲染了
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Test1 : MonoBehaviour
{
void Start()
{
//1.获取mesh对象
Mesh mesh = GetComponent<MeshFilter>().mesh;
//2.获取mesh的顶点坐标数组
Vector3[] vertices = mesh.vertices;
for (int i = 0; i < vertices.Length; i++)
{
Debug.Log(vertices[i]);
}
//3.获取mesh的索引数组
int[] triangles = mesh.triangles;
for (int i = 0; i < triangles.Length; i++)
{
Debug.Log(triangles[i]);
}
triangles = new int[12] { 0, 3, 1, 3, 0, 2, 0, 1, 3, 3, 2, 0 };
mesh.triangles = triangles;
}
}
效果:
三、绘制正方形
看了上面的案例,如果是第一次接触 Mesh 肯定有很多的疑惑,比如说 triangles 这个变量,它的值是一堆无序的整型数字,为什么要这么写呢?它的作用又是什么呢?
triangles = new int[12] { 0, 3, 1, 3, 0, 2, 0, 1, 3, 3, 2, 0 };
要搞明白这个问题,还是得以各种小案例来慢慢讲解这些知识点。
在默认的场景中,创建一个空物体,取名为 Root,将位置坐标设置为0,并将视角调整为从上往下看(鸟瞰视角),那么在场景中就是这样的:
从场景中可以看到有很多的格子,其中 Transform 的值中 1 代表 1 格,也代表游戏场景中 1 米的距离,在后面的案例中,顶点坐标的位置也是以此作为标准
接下来创建一个脚本 Test3,将其拖到 Root 上。
1.显示顶点坐标
为了理解顶点坐标在三维空间是怎样的,下面我用四个球来表示四个顶点的位置
第一个球的位置为:0,0,0,它所在的位置是世界坐标的零点。
第二个球的位置为:0,1,0
第三个球的位置为:1,0,0
第四个球的位置为:1,1,0
我们分别给这四个球添加材质,颜色按照顺序为:红、绿、蓝、紫,大家可以记一下这几个颜色的顺序,因为后面会涉及到渲染正反面的问题。
这时候场景中如下:
从侧面45°视角看是这样的:
运行后在 Game 视图,排在上面的两个球只显示了一半,为了让三个球显示完整,我将摄像机的位置向后拉了一些,Z 轴设置为 -2,这样就能将四个球完整的显示了。
2.顶点坐标的顺序
由于 Unity 在一般只渲染一个面,顶点坐标的顺序会影响到渲染的是正面还是反面,这对理解 Mesh 是至关重要的,最好去亲自动手操作一遍,加深印象。
下面这个图就是一个平面顶点坐标的顺序,在 Unity 中,一个平面是由两个三角形组合而成的,左边三角形的顶点顺序为:0,1,2,右边的三角形的顶点顺序为:2,1,3,可以理解为一个顺时针的顺序进行排列的。
我们先来渲染左边的三角形,那么数组可以这么写:
Vector3[] vertices = new Vector3[]
{
new Vector3(0, 0, 0),
new Vector3(0, 1, 0),
new Vector3(1, 0, 0),
};
那么,它的下标应该这么写:
int[] triangles = new int[]{ 0, 1, 2 };
3.顶点排序
如果说顶点 Vector3 坐标需要按照严格的顺序进行排列,那么顶点的下标如果不按照这个顺序会有怎样的效果呢?下面就来测试吧。
首先,先按照正常的顺序:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Test3 : MonoBehaviour
{
void Start()
{
GameObject obj = new GameObject();
obj.name = "TestMesh";
obj.transform.position = Vector3.zero;
//网格渲染器
MeshRenderer meshRenderer = obj.AddComponent<MeshRenderer>();
meshRenderer.material = new Material(meshRenderer.material);
//网格过滤器
MeshFilter meshFilter = obj.AddComponent<MeshFilter>();
//顶点坐标
Vector3[] vertices = new Vector3[]
{
new Vector3(0, 0, 0),
new Vector3(0, 1, 0),
new Vector3(1, 0, 0),
};
//顶点下标顺序
int[] triangles = new int[]{ 0, 1, 2 };
//用列表数据创建网格 Mesh 对象
Mesh mesh = new Mesh();
//设置顶点
mesh.SetVertices(vertices);
//设置顶点索引
mesh.triangles = triangles;
// 自动计算法线
mesh.RecalculateNormals();
// 自动计算物体的整体边界
mesh.RecalculateBounds();
// 将mesh对象赋值给网格过滤器,就完成了
meshFilter.mesh = mesh;
}
}
运行:
从上图可以看到,按照正常的下标顺序,结果是对的,如果将顶点下标的顺序颠倒过来,会发生什么呢?
int[] triangles = new int[]{ 2, 1, 0 };
运行:
这时,正面看不到三角形了,将视角转到反面
反面虽然能看到三角形,但是颜色有点暗。
这其实是灯光的问题,默认的全局环境光位置在正面,所以背面的光线就比较暗。
我们先关闭编辑器的运行,将灯光再复制一份
将坐标向后拉一点,为了就是让灯光照到背面
再将旋转角度设置为 150°
再次运行后,从背面也能看到和正面一样的效果了
在这里,下标顺序换成 2,0,1 也能正常的渲染
int[] triangles = new int[]{ 2, 0, 1 };
效果:
继续换一个,换成 1,2,0 效果也是一样的。
int[] triangles = new int[]{ 1, 2, 0 };
运行:
虽然下标顺序打乱也能实现效果,但是并不推荐这么做,要想渲染一个正方形,最好是按照:0,1,2,2,1,3 这种顺序进行渲染。
4.绘制最终效果
理解了上面的知识点,想要绘制一个正方形就简单了,代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Test3 : MonoBehaviour
{
void Start()
{
GameObject obj = new GameObject();
obj.name = "TestMesh";
obj.transform.position = Vector3.zero;
//网格渲染器
MeshRenderer meshRenderer = obj.AddComponent<MeshRenderer>();
meshRenderer.material = new Material(meshRenderer.material);
//网格过滤器
MeshFilter meshFilter = obj.AddComponent<MeshFilter>();
//顶点坐标
Vector3[] vertices = new Vector3[]
{
new Vector3(0, 0, 0),
new Vector3(0, 1, 0),
new Vector3(1, 0, 0),
new Vector3(1, 1, 0),
};
//顶点下标顺序
int[] triangles = new int[] { 0, 1, 2, 2, 1, 3 };
//用列表数据创建网格 Mesh 对象
Mesh mesh = new Mesh();
//设置顶点
mesh.SetVertices(vertices);
//设置顶点索引
mesh.triangles = triangles;
// 自动计算法线
mesh.RecalculateNormals();
// 自动计算物体的整体边界
mesh.RecalculateBounds();
// 将mesh对象赋值给网格过滤器,就完成了
meshFilter.mesh = mesh;
Debug.Log(string.Join(",", triangles));
}
}
效果:
继续将 Shading Mode --> Shaded 改为 Shaded Wireframe
就能看到模型对应的三角形面和中间的那条斜线了,现在回到上面的第二章节看看,Quad 和 当前案例中的斜线的方向是不是不一样呢?
结束
如果这个帖子对你有所帮助,欢迎 关注 + 点赞 + 留言
end