从图形学认识Unity中的Mesh

本文从图形学角度探讨Unity中的Mesh,包括图元定义、顶点位置、索引、Unity的MeshFilter和MeshRenderer组件。通过示例代码,详细解释了如何创建和使用Mesh,以及Mesh在Unity中的应用,如MeshCombine实现网格合并。
摘要由CSDN通过智能技术生成


前言

此文从图形学的角度讲解了什么是顶点,以及Unity中的Mesh是如何组织顶点并利用什么组件对它们进行绘制的。

  • 内容比较基础,如果需要深入,建议学习基本的图形学知识
  • 偏理论,如果需要更多的代码和运行结果,请在评论区告诉我!
  • 文中的示例代码以上传至github(版本2019.1.8f2) https://github.com/UnknownArkish/UnityMeshDemo

图元的定义Primitive

简单来讲,图元就是组成图像的基本单元,比如三维模型中的点、线、面等等
https://baike.baidu.com/item/%E5%9B%BE%E5%85%83/2303188?fr=aladdin

图形学的一个环节是建模,这里的建模和美术所说的建模类似,主要在于如何表示一个物体。

计算机中,要绘制一个物体和现实生活中一样,由点构成线,再由线构成面,最后由若干个面构成一个物体。
例如下图中,左图利用三个点绘制了一个三角形,而右图则通过两个三角形得到了一个四边形:

注:这里需要注意的是,很明显从左图中给定的三个点,有两种方式可以得到三角图元,即

  • 顺时针旋转(也称左手螺旋):p0->p1->p2
  • 逆时针旋转(右手螺旋):p0->p2->p1

它们的区别在哪里呢?类似于物理中左右手判断法则,拇指指示了三角图元的法线方向。法线其中一个作用是指明了三角图元的方向,如果从反方向看的话,这个图元是不可见的。(Unity中的Plane同理,只有一面可见)
法线另外的作用可以用来计算光照,不过那不在本文的讨论范围内,感兴趣的可以了解相关的图形学知识)

更复杂一些,即使是美术建模的模型,从图形学的角度上看,其实也就是一堆三角面片的集合:
图源水印,侵删

因此,要表示一个物体,使用三角(triangle)图元作为基本元素就足够了。
(*注:图形接口(如OpenGL和DirectX3D)可能会提供其他规格如四边形的基本图元绘制的规则,但实际上出于统一、直观的原因,一般采用Triangle而不是Quad)

从数学角度表示图元

既然上面已经对图形的基本元素图元(primitive)有了一定的认识,那么图元究竟如何在计算机中表示呢?
其实上面的图(第一张图)已经暗示了,可以使用三维坐标来表示每一个点的位置。

位置

假设我们有Vector2类,它表示一个二维坐标。(三维坐标系同理,只是多了一个维度z)
那么要绘制上图中的三角形,即问题在于表达三个点的坐标,也就可以表示为下面的代码:
(注: 数值无意义,仅表示数值1,单位需要定义)

Vector2[] vertices = new Vector2[]{
    
	new Vector2( 0, 0 ),							// p0
	new Vector2( 0, 1 ),							// p1
	new Vector2( 1, 0 ),							// p2
 };

而如果要绘制四边形,也就需要六个点的坐标,如下面所示:

Vector2[] vertices = new Vector2[]{
    
	// 三角图元 0
	new Vector2( 0, 0 ),							// p0
	new Vector2( 0, 1 ),							// p1
	new Vector2( 1, 0 ),							// p2
	// 三角图元 1
	new Vector2( 0, 1 ),							// p1
	new Vector2( 1, 1 ),							// p3
	new Vector2( 1, 0 ),							// p2
 };

事实上这些数据在OpenGL中,就是VBO(Vertex Buffer Object,顶点缓冲对象)

索引

如果单纯使用顶点来表示(绘制)图形,是非常直观的——因为我们知道每一个顶点的位置信息,并且知道每三个点构成一个三角图元。然而从上面绘制四边形的数据中可以发现,三角图元1中有两个顶点的位置信息(p1和p3)和三角图元0是一样的。这也就增加了一些开销。
假设有n个面,而且每个面之间都是两两相连的(这要求n>=3),那么也就会多增加 2 * n个顶点的开销。如果是三维的Vector3(3 * 4 byte= 12 byte),将会增加 2 * n * 12 byte = 24 * n byte的开销。而一个模型少说都有上千个面,更不用说一个顶点不止保存有位置信息了(其他还会有法线信息,贴图纹理坐标信息等)。

总而言之,这个额外的存储开销是完全可以去除的,方法就是使用索引(Indices)。
简单来说,我们仍然使用vertices存储顶点的位置信息,但是vertices只存储不重复的部分,改为使用vertices数组的indices来表示三角图元。听上去有点拗口,那么就来看代码吧:

Vector2[] vertices = new Vector2[]{
    
	new Vector2( 0, 0 ),							// p0
	new Vector2( 0, 1 ),							// p1
	new Vector2( 1, 0 ),							// p2
 };
 int[] indices = new indices[]{
   
 	0, 1, 2											// 表示 0->1->2 构成一个三角图元
 };

没错,indices就是这么一回事,原来是vertices数组中,每三个顶点表示一个三角图元,现在是indices数组中,每三个整型表示一个图元。这些整形不存储真正的数据,只有用到时才从vertices中取出,也就是所谓的索引了。
(你可能会觉得比起原来的,这不是额外增加了indices的开销吗,硬要回答的话,emm只能说这个是特殊情况)

为了让上面的indices不那么蠢,那么接下来看看原来的四边形,应用了indices后是怎样的:

Vector2[] vertices = new Vector2[]{
    
	new Vector2( 0, 0 ),							// p0
	new Vector2( 0, 1 ),							// p1
	new Vector2( 1, 0 ),							// p2
	new Vector2( 1, 1 ),							// p3
 };
 int[] indices 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值