《Unity3D高级编程之进阶主程》第五章,3D模型与动画(四) - 3D模型的变与换1

3D模型是3D游戏中的基础单位,除了在游戏中那些静止的一动不动的3D模型外,还有许许多多动来动去的,这些3D模型除了骨骼动画,顶点动画外,很多时候为了画面效果的表现,我们还需要:切割,简化,变形,捏脸,飘动等等。那么它们是怎么实现的呢?我们今天就来讲讲这蕴含其中的技术与原理。

===

一个3D模型是如何显示到屏幕中去的,这整个过程详细讲解下来有点长,所以将它们放在了OpenGL渲染管道章节中,这里我们先来做个不太深入的了解。

众多的顶点构成了一个3D模型,顶点之间的连线组成了三角形或多边形,其实还是以三角形为主。为什么?因为OpenGL,DirectX都以三角形为单位渲染。至于为什么要以三角形为单位进行渲染,会在OpenGL渲染管道章节中详细讲解。

这么多的顶点是怎么表达三角形的呢,有很多种方式,例如顶点索引,三角带,三角扇,不过这里我们只说说普遍用的比较多方式,也就是顶点索引方式,其他方式留到渲染管道章节去详细剖析。

顶点索引是主流的三角形表达方式,其他方式都是为了优化索引这个数组而设计出来的,应用的范围相对小一点。

顶点索引的方式就是把所有顶点放进一个数组里,然后用另一个数组用来表达三角形的组成(就是用顶点数组里的index下标)。在索引数组里,每3个索引组成一个三角形,也就是说,如果有4个顶点的数组表达了一个正方形的形状,这个正方形相当于有两个三角形组成的,索引的数组就是大小就是6,前3个索引数组表达第一个三角形,后3个数组表达了另一个三角形。

看数据:(0,0,0), (0,1,0), (1,1,0), (1,0,0) 这个4个顶点构成了正方形。

那么索引数组就是,0,1,2,2,3,0 ,其中前三个0,1,2构成一个三角形,后三个2,3,0构成另一个三角形。

依次类推,每三个索引单元描述三角形的三个顶点。

我们把所有顶点和所有顶点索引都传给显卡驱动,显卡驱动就可以根据这些数据来展示3D模型,这中间并没有这么简单,不过也并没有复杂到不可理解,只是多了些陌生的的流程和步骤而已,熟悉下就可以理解和应对了,这些都将在OpenGL渲染管道章节中,由浅入深的剖析。

那么图片是怎么贴上去的呢?

如果把3D的三角形当做一个2D的面片来看就会好理解多了,一个2D的三角形面片为了把图片贴上去,就需要在图片上也指定三个点,图片上的三个点形成了与三角形一样比例大小的形状,就可以贴上去了,如果比例不对称也可以贴,只是会拉伸或缩放而已。这三个在图片上的点坐标就叫u和v,都是2个数字的数组(都是Vector)为什么叫uv而不是传统的xy,因为不想和xy概念混合,而且uv是一个0到1的浮点数,0表示起始位置,1表示最大偏移位置,一听uv二字母就知道说的是图片上的坐标。

以这种三角形贴图的方式贴到3D模型的每个三角形上,就可以如期的绘制出有’皮‘的3D模型了。

这样一来,在绘制3D模型时,就多了另一个数组,叫uv数组,这个uv数组是由uv坐标组成的,由于已经有了顶点索引来表达三角形,所以uv数组就不再需要索引来表达了,只需要按照顶点的索引形成的三角形来定制uv的顺序就可以了。

还是用旧数据举例来说明:

[(0,0,0), (0,1,0), (1,1,0), (1,0,0)] 这个4个顶点构成了正方形。

[0,1,2,2,3,0] 组成了顶点索引表达了2个三角形的形成。

[(0,0), (0,1), (1,1), (1,1), (1,0), (0,0)] 组成了uv数组,表达了两个三角形上贴图的绘制范围。

别小看这些基础知识,这些基础知识在平时的实践中有很大很大的用途。下面就开始介绍我们在具体实践中,是如何将这些基础知识灵活的运用到项目中去的。

1.切割模型

模型切割,也可以说模型的分裂,在游戏中也比较常见。我们这里不具体深入不同切割的算法,因为项目的每种切割方式都可以是不同的,可以由不同的算法代替。我们只说如何切割模型,就拿最简单的一刀切割的方式,如’切水果游戏‘那样,横向或纵向切割。

我们知道一个模型是由一个渲染实例构成的,也就是说一个Render组件(MeshRender或者SkinnedMeshRender,这里统一称为Render组件)只能渲染一个模型。

那么要把一个Render渲染的模型切割成2个,就相当于把这个渲染组件删掉,生成两个新的Render渲染组件,渲染不同的模型。

有了这个方向就容易多了。我们可以把原来的Render渲染组件中的顶点数组、顶点索引数组,uv数组,都提取出来,并全部都分成两部分,一部分是切割后的左半部分,另一部分是切割后的右半部分。再把这两部分,分别放入新建的两个新的Render渲染组件实例中去,就得到了切割后的模型。

假如在切割后再对这两个切割后的模型,加入碰撞体和物理运动组件(或者说重力引擎Rigidbody),就可以让画面表现的像是真实的被切割开来后倒地分成了两半一样。

这其中,核心的难点是如何拆分成两部分,以什么规则拆分,如何知道这个顶点在左半边还是右半边,切割中如何生成新的切割顶点,如何将这切割顶点缝合。

首先面临的是怎么区分点在左半边还是右半边,我们不用区分是左边还是右边,只要区分是否是在平面的同一侧就可以了,方法有很多,比如点与平面上任意点组成的线段和平面上任意线的点积来判断是否为相同一侧,也可以通过平面与线段的交点之间的连线与点与交点的连线的点积判断是否为同一侧。

其次我们用平面与线段是否有交点的图形算法,来判定是否有新交点。把所有三角形的线都与平面做交点碰撞测试,如果有碰撞,则交点就是新切割点。得出的新切割点用处很大,而且要复制两份,一份放在左半部分,一份放在有半部分。

切割后,原本点与点之间相连的映射关系被打破并转移到了新交割点上,于是旧点与新切割点组成了新的三角形,左右两部分都是同理。

用数组的方式更容易理解点,还是拿上面的数据来举例。

[(0,0,0), (0,1,0), (1,1,0), (1,0,0)] 这个4个顶点构成了正方形。

切割后分成了两个数组,假设切割点为中点对半切开,也就是切割点位(0.5,0.5,0)和(0.5,0,0)。

此时数据就变成了这样

[(0,0,0), (0,1,0), (0.5,0.5,0), (0.5,0,0)]

[(0.5,0.5,0), (0,0.5,0), (1,1,0), (1,0,0)]

这个举例的例子是相对比较简单的实例,不需要缝合切面。

如果切割的对象是一个立方体或者更复杂的模型,切割后,就需要缝合切割面。因为缝合的面上所有点是在同一平面的,所以缝合时只需要缝合新生成出来的交点部分就可以了,因为其他部分还是保留原来的样子。

缝合的算法很多,主要的目的是,将多新生成出来的点,有规则的组成新的三角形,进而形成一整个切割面。

其中一种算法是,先选一个点,再用这个点与其他点形成的线段算出夹角,用夹角的大小进行排序,排序后的结果,就是顺时针或者逆时针的点位,然后按顺序先将靠最前面的前三个点形成一个三角形,后面的点位与它之前的两个点形成新的三角形,也就是第4个点与前两个点也就是第三个点和第二个点形成三角形,第5个点与第4个和第3个点形成新的三角形,依次类推形成切割面。

2.扭曲模型

扭曲模型,相当于将3D模型变形,凹进去,凸出来,部分放大,或者部分缩小等。

有了上面介绍过的3D模型的基础知识,这里在编写模型的变形时就显得更加容易。

3D模型就是由三角形组成的,三角形是由点组成的,要变形就是移动顶点的位置,不只一个顶点的位置,是一片顶点的位置,或者说一个范围上的点的位置。

扭曲和变形在实际项目中也有很多应用,比如爆炸后的地面凹陷,击打某墙壁后的墙壁有个凹陷的表现,或者拉某个球时球有个先被拉伸再恢复的过程,或者3D模型表现制陶工艺,将模型从一个罐头转转转,拉拉拉,拉成一个自定义的陶瓷品,也些角色扮演类游戏中,操控的摇杆就是用可拉伸的泡泡糖表现的,这些都是对模型内某个范围或者某些顶点的进行位移后表现出来的。

顶点的点位的移动相对比较简单,就是取出顶点数组,修改坐标,再放进去,三个步骤。

难点一是如何正确找出需要修改的顶点,二是不同的点修改的值如何不同,找出修改方向的算法,三是持续修改的算法。

我们拿爆炸凹陷,球体拉伸反弹,制陶工艺这三个例子来剖析如何扭曲模型的技巧。

当发生在场景中发出爆炸特效时,首先找出爆照范围的这块地面,并取出这块地面的所有顶点数据,对所有顶点求出在爆炸范围球体内的地面顶点,这些顶点就是需要修改的顶点。

爆炸需要凹陷,凹陷算法的目的是把顶点位置修改到爆炸球体的球表面上,这个算法相当于,如何把一个点对应到一个球体的面上,计算出来后修改点位置,再装入渲染的实例中,凹陷变形就完成了,顶点索引和uv都不需要任何变化。

再说说球体拉伸与反弹恢复,第一个动作是拉伸球体,计算所有顶点与要拉的那个点位的距离,距离越大,顶点需要的位移的越小,这个比例肯定不能是个正比关系,一定是一个衰减的曲线,例如距离是d,拉伸的距离为f,结果为res,最简单的衰减公式为 res = f/(d * d),用这个公式对每个顶点进行计算,得到一个需要移动的数据res,这个数据是根据距离大小而衰减的,与拉伸点离得越近移动量越大,反之越小。修改完成所有顶点的坐标后,再推入渲染实例中去,就得到了球体拉伸的效果。

球体的第二个动作是放开后恢复,这里我们假定球体的屁股是被固定的。恢复和拉伸有点相似,拉的最远的反弹的快,也就是与原有的位置距离最大的点反弹的速度最大,也就是说,我们需要记录原来没被拉伸时的顶点的坐标位置,他们与原来的位置相减就是反弹速度的基础变量。

反弹力度肯定也不是正比关系,肯定也是类似衰减公式的增强公式,或者说是弧线比例,最简单的公式就做个平方,res = d * d,或者为了更平滑点,找个更好的曲线公式res = (d / max) * (d - 2) * k。

反弹恢复的力度,在不断得计算过程中,会由于顶点与原有的点位的距离缩小而减小,后又由于反弹过度而不断放大,有一个来回反弹的过程,最后恢复到平静不再移动的状态,用这个公式能就很好的体现出来,因为它与原顶点距离有关,力度在不断得衰减,最后形成稳定态。

最后再说说制陶工艺的模拟,一个罐体模型在转盘上不停的转,当用手(鼠标或者触摸屏)去触摸它的时,在触摸的点会形成凹陷,或者拉伸,人们通过在这样不断的凹陷和拉伸过程中制作出了一个完整的陶瓷的模样,这就是制陶工艺的过程。

在一个叫做《釉彩》的手机App中有具体的表现,里面你可以用一个很丑的泥罐,通过来回、上下、左右的手指滑动制作出一个你喜欢的陶瓷品,制作出来的陶瓷品可以让别人定做,也可以通过里面的超市直接购买。

它的原理非常简单,就是在当你触碰时,根据你的手指滑动的方向,把范围内的顶点向手指滑动方向偏移,并且有一个衰减范围,离手指最近的点越近拉伸的距离越大,离得越远拉伸的距离越小。顶点选取的范围的判定可以认为是在一个矩形中的范围,比如认定的手指滑动的矩形范围,从而构建出一个相应的立方体范围,进而选出在立方体内的顶点,再进行衰减式的位移,最终构建出,可上下,左右,对陶瓷罐的拉伸变形操作。

由于篇幅限制,下篇将继续介绍’3D模型的变与换2‘。

感谢您的耐心阅读

Thanks for your reading

  • 版权申明

    本文为博主原创文章,未经允许不得转载:

    《Unity3D高级编程之进阶主程》第五章,3D模型与动画(四) - 3D模型的变与换1

    Copyright attention

    Please don't reprint without authorize.

  • 微信公众号