目录
一、概述
二、获取顶点坐标和索引
三、绘制正方形
1.显示顶点坐标
2.顶点坐标的顺序
3.顶点排序
4.绘制最终效果
结束
一、概述
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,并将视角调整为从上往下看(鸟瞰视角),那么在场景中就是这样的:
在这里可以看到,场景中有很多的格子,其中 1 格代表 Position 中 1 米的距离,在后面的案例中,顶点坐标的位置也是以此作为标准
创建一个脚本 Test3,将其拖到 Root 上。
1.显示顶点坐标
为了理解顶点坐标在三维空间是怎样的,下面我用四个球来表示四个顶点的位置
第一个球的位置为:0,0,0,是世界坐标的零点。
第二个球的位置为:0,1,0
第三个球的位置为:1,0,0
第四个球的位置为:1,1,0
我们分别给这四个球添加材质,颜色按照顺序为:红、绿、蓝、紫,这里可以记一下这几个颜色的顺序,因为后面会涉及到渲染的正反面问题。
这时候场景中如下:
从侧面45°视角看是这样的:
运行后在 Game 视图,最上面的两个球只显示了一半,为了让三个球显示完整,将摄像机的位置向后拉了一些,Z 轴设置为 -2,这样在 Game 视图就能将四个球显示完整了。
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