“风吹仙袂飘飘举,犹似霓裳羽衣舞。” ——《长恨歌》 白居易
0. 前言
项目技术测试结束之后,各种美术效果提升的需求逐渐成为后续开发的重点,角色效果部分的提升目标之一便是在角色选择/展示界面为玩家提供更高的品质感,于是可以提供动态效果的布料系统就作为了技术预研的方向。笔者在之前的端游项目中有较多使用布料系统制作布料效果的经验,这次也尝试了在Unity引擎中进行布料效果制作的几种方法。本文整体框架基于之前整理的一篇关于布料系统基本原理的文章,结合Unity引擎中的制作方法,为需要在手游上有更好的角色动态表现需求的项目提供一些方向和思路。
1. 什么是布料系统
随着时代的发展和移动设备硬件性能的提升,物理引擎被越来越多的移动游戏所使用。物理引擎处理的对象主要可以分为刚体(Rigid Bodies)和柔体(Soft Bodies)两大部分,刚体主要应用在物理碰撞、破碎、布娃娃等系统中,而柔体最为重要的应用代表就是布料系统。“风吹仙袂飘飘举,犹似霓裳羽衣舞”
,白居易《长恨歌》中描绘的美丽情景就是布料系统所希望实现的游戏效果之一。笔者就着眼于布料系统,从其相对于传统制作方法的优势出发,描述其基本的实现原理,以及对常见的几种带有布料系统的物理引擎进行简单的对比,最后以目前在开发的手游为例,聊一聊使用Unity引擎制作手游上可以应用的角色布料效果的基本方法。
在物理引擎中,通过物理计算来模拟布料效果的系统可以称之为布料系统。虽然名为布料系统,但并不仅仅用于表现角色衣服的布料效果,而且可以应用于毛发、坠饰,甚至女性的某些柔软部位……下面的几张图就给出了一些游戏中应用了布料效果的地方。
目前来说,大部分3A级别的主机游戏中,布料效果已经几乎是标准配置了,而在国内的端游中,也已经得到了大范围地应用。而在手游中,只看到为数不多的几款在小范围地使用,比如只有在角色选择界面,或者只有主角身上会开启部分布料效果。
2. 为什么要使用布料系统
如果把为什么要使用布料系统这个问题的答案归结为策划和美术需求的话,就未免太过肤浅了。在角色身上或者场景的组件中表现布料的效果,比如上面图中角色的裙摆,场景中迎风招展的旗帜,都可以增加游戏的真实感,提升美术效果。但基于物理引擎的布料效果的具体优势还要从与传统做法的对比说起。传统的布料效果实现方式非常简单——美术手动制作的动画。
在物理引擎被广泛使用之前,游戏中的绝大部分动态效果都是通过固定的动画来表现。这个做法非常容易理解——角色身上的飘带也好,头发也好,在动画师的眼中,与角色的手脚四肢没有任何区别,添加骨骼,进行蒙皮,制作动画,无论一帧一帧自己手K还是用Max的柔体效果插件生成,都可以制作出逼真的布料运行的效果。(在更早的使用网格动画的年代,原理也相差不多。)如果你觉得效果不够好?那简单,再多加一些控制骨骼,多给美术一些时间,就可以做出更好的效果。
基于动画的制作方法虽然可以满足大部分的需求,但随着时代的发展,无论玩家还是游戏开发者都在追求更好更真实的效果,这种制作方式表现出来的布料效果的缺点慢慢显现出来:
1. 布料效果不够真实
预先制作的动画是一份确定的数据,无论在什么情况下只要播放这段动画表现出来的效果都是完全一样的。这种稳定性在某些情况下并不是一种真实的表现,比如一个很简单的例子是角色的原地转身,通常情况下这种转身是由程序改变模型朝向来实现了,动作依然是Idel这样的动作,本来柔软的布料应该表现出应有的惯性,但动画系统很难做出相应的表现。 另外,虽然在单个的动画中,布料的表现效果足够好,但是在各个动作之间转换,甚至多个动作融合到一起进行表现的时候,用于表现布料效果的骨骼融合结果就会不那么自然,真实感也就大打折扣。
除此之外,当你灵巧的躲过一个敌人的箭矢,感受到敌人箭矢从耳边呼啸而过的惊险与刺激的时候,箭矢所产生的风如果可以吹起你的衣服是否更带感呢?传统的动画效果也几乎无法实现这样的需求。
2. 无法应对越来越多的变化
越来越多的游戏提供给玩家一个沙盒的世界,也就意味着玩家可以控制角色做出越来越多游戏设计者不可控制不被预期的行为和动作表现。在这种情况下,基于骨骼动画的布料表现就有些力不从心。比如当角色有移动速度的变化时,我们通常可以通过改变动画的播放速度提供对应的表现,但是移动速度很快和移动速度很慢时,裙摆头发的飘扬效果都是完全一样的,甚至当玩家面对一堵墙按下移动按钮,角色开始播放跑步动画,虽然并没有移动,但仍然可以看到角色的裙摆和头发在迎风飘扬……
3. 限制时装的设计和制作
目前大部分游戏在设计角色的时候由于要考虑到角色未来可能会出的时装,可能采用最大化骨骼的制作方案。所谓最大化骨骼,就是在角色制作初期就确定好角色最多可能有哪些骨骼,在之后制作角色时装的时候,要完全在最初定义的最大化骨骼范围内。如果超出了这个范围,就需要修改所有时装的模型以及角色的所有动作,并全部重新导出,这种修改的代价是非常大的。
这些预留的最大化骨骼,有很多是在角色默认设计的时装中没有被使用的,但是为了给未来预留更多的可能,因此需要留好控制裙摆、长发等部件的各种最大化骨骼,而且注意,这些骨骼上都需要制作对应的动画效果,或者在未来制作某套需要这一骨骼的时装的时候,把所有的动画添加上对应骨骼的动作,重新导出。在美术工作量、美术资源的大小、游戏的运行效率等方面,这种做法都有一定的负面影响。
当然,大部分引擎都已经支持了子模型的方式,也就是引擎可以把一些子模型动态地合并到一套骨架上。但是《剑灵》的制作人员提供了另外一种思路——主角的动画只制作身体主要部位的动画效果,对于所有的裙摆、坠饰这样的物体,都统一使用布料系统来制作。这一制作方案可以完美解决设计角色时装时要考虑是否有骨骼可以驱动的问题,从而解放美术的想象力,节省美术的冗余工作,运行时的效率也得到一定的提升。
4. 无法支持动画重定向之后的动作
动画重定向是一种常用的动画复用技术,可以让一个模型A使用另外一个模型B的动画数据,但是如果模型A带有裙摆、长发这样的额外部分,而模型B的动画中并没有驱动对应网格的骨骼,那么重定向之后的裙摆就会非常僵硬——大多数情况下它们以T-pos的姿势跟随父骨骼进行整体的移动。
很多格斗游戏在受击效果中会大量使用了动画重定向技术,要么让动画模板包含所有可能的骨骼信息,要么让那些不被驱动的面片就呆在那里不动。前者几乎无法实现的,因为对于非CS骨骼来说,不同的骨骼名称在不同的模型中对应的部分可能是不同的,比如Bone001在A角色中对应驱动头发部分的动画效果,而在B影响中驱动了裙摆,即使强制要求美术按照统一的规则进行命名,这个工作量和出错的概率也是非常大的,那就需要一套包含所有角色最大化骨骼的“最大化最大化骨骼”。而后者可能是策划与美术无法接受的。
总之,基于物理模型的布料系统提供一套在游戏运行时根据环境计算布料效果的解决方案,这一系统可以有效地弥补传统动画制作方式的上述这些不足,为游戏提供具有更强真实感的布料效果。
3. 布料系统的实现原理
在应用布料系统之前,我们先浅显地看一下布料系统的发展过程和基本原理。
在图形学领域开始布料模拟的研究可以最早追溯到上世纪八十年代,最早的时候学术界对于布料系统的研究肯定不是用于布料的实时计算,而是离线渲染,甚至只是为了生成一副静态的图。比如Terzopoulos在1987年的论文提出一种方法,将布料当作矩形的网格,然后使用半隐式集成的方式来更新它的位置,使用弹性理论来模拟动画效果。Breen在1994年提出了一个新的视角,即基于粒子(Particle-based)的方案,使用用于模拟纤维织物的Kawabata measuring system来模拟布料的运行效果。Baraff和Witkin在1998年发表的论文中提出了一种在保证布料模拟效果稳定的前提下允许更大时间步长的方案,这一方案也被逐渐地改进为更快的方法。在这之后,基于AABB树等数据结构的算法优化,与刚体的碰撞,布料自身的碰撞计算等问题逐渐被研究和提出相应的解决方案,布料模拟也逐渐从一个学术界的研究方向发展成为可以应用于影视制作的工业技术,进而应用于需要实时计算的游戏领域。下面的几幅图给出了几个重要的论文在当年所展示的布料模拟效果。
布料模拟技术大致由布料建模、数值计算、碰撞处理和渲染显示四大部分组成。从前文可以看出,在30多年的研究发展中,各国的研究人员针对这些技术提出了多种不同的方法,在这其中,布料建模是最为重要的部分,因为它影响甚至决定了后面的数值计算和碰撞处理的实现机制。被提出以及在应用的建模方法有很多种,比如较早的悬链线性模型、纯集合变换的布料变形模拟方法,基于物理的弹性变形模型,粒子系统模型等等,甚至也有基于纹理生成几何褶皱的方法。而现代游戏引擎中实现和集成的模型通常是基于物理方法的质点-弹簧模型(Mass-Spring Model),这一方案具有模型简单且运算效率较高的特点,我们着重讲述一下这一模型的基本原理。
在质点-弹簧模型中,布料被当做网格上的粒子来进行模拟,这些粒子之间由弹簧减震器进行连接。每一个弹簧连接两个粒子,并且基于粒子的位置和速度来产生作用力。粒子可以受重力影响,弹簧可以设置为不同的类型,比如拉伸弹簧(stretch springs)、剪切弹簧(Shear Springs)和弯曲弹簧(bend springs)等。
作用力的计算过程主要包括两部分,一个是质点所受的重力:
另外一个弹簧对于质点的作用力,我们使用Ks来表示弹簧本身的约束力,Kd表示阻尼因子,l0表示弹簧的静息长度,那么根据物理原理得到作用力的计算方程如下:
当我们计算出这一模型中的所有作用力之后,就可以根据牛顿第二定律计算每个粒子的加速度:
an是粒子n的加速度,fn是作用于粒子n的作用力,mn是粒子n的质量。当我们计算好加速度之后,我们可以用它来模拟一个很小的时间步长内的速度和位置变化:
这就是最简单的质点弹簧模型的基本原理。其他更多的改进方案,更多的效果支持,都是基于这一简单的物理模型来实现的。通过修改质点的质量参数,弹簧的类型以及阻尼因子、静息长度等参数,就可以实现丝绸、麻布,甚至竹席等不同的布料效果。当然,要实现一套真正可用的完整模型和可以支持运行时计算的高效模拟算法,还需要很多问题和细节要去解决,这些在本文中就不再进行赘述,有兴趣的同学可以参考Cloth Simulation相关论文或者阅读开源引擎相关源码。
4. 游戏中成熟的布料系统引擎
在了解了布料系统实现的基本原理之后,我们来看一下常用的几款提供了布料系统实现的引擎。其实对于布料系统,甚至物理系统来说,很多引擎都不会自己去实现,而是集成已经提供了相应功能的中间件。世界三大物理引擎有AMD公司的Bullet、Nvidia家的PhysX,还有现在被Microsoft从Intel手中收购了的Havok,他们都提供了整套的布料模拟解决方案供其他引擎集成和使用。
Bullet作为一个跨平台的开源物理引擎,当然支持布料模型的创建和模拟,但似乎没太听闻哪些游戏大作使用了这一引擎制作布料效果然后大肆宣传,因此在游戏行业内的应用不如另外两款多。由于笔者对这一引擎了解不多,在此不进行详细的介绍。这款引擎的一大好处在于开源,想了解布料系统实现细节的同学可以直接翻看源码来进行阅读和学习。
PhysX提供了功能强大的布料系统,Unity中的物理引擎部分就是基于PhysX搭建的,Unreal引擎同样默认集成了PhysX,笔者猜想这是受英伟达自由许可政策的影响——“PhysX 二进制软件开发包可免费用于非商业和商业游戏与应用程序(这意味着,没有许可费或特许使用费)”。除了一些使用Unreal引擎的主机大作基于PhysX制作布料效果,国内腾讯公司的《天涯明月刀》中武侠风的布料效果也是基于PhysX来制作的,他们也以此作为一个卖点进行推广。
在PhysX引擎中,除了布料的模拟和碰撞之外,已经支持了布料的破碎(Cloth Tearing)效果。这种效果要求当模型中的弹簧被拉伸得长度超出一定限度之后,可以破坏掉原来的模型结构。
Havok也是一款被广泛应用的物理中间件,很多3A大作的各种物理效果都是基于它进行的制作。Havok提供了Max和Maya等多种建模工具的插件来制作并导出布料信息,运行时通过物理计算实现质点弹簧模型的更新过程。下图是Havok 2015版本的Demo视频中的效果图,虽然还不支持破碎等效果,但是在碰撞检测的精度、运行效率等方面有了不小的改进。
5. Unity中的布料制作和应用
笔者之前所经历的端游项目中使用的是Havok作为物理引擎来制作的布料效果。Havok引擎提供了一套非常丰富而又复杂的工具链来支持布料制作功能,当时为美术翻译(是的,即使是大厂的美术对于英文文档也是抗拒的)、编写的使用方法就有几十页,美术甚至需要通过组织沙龙来分享布料效果的制作方法和经验。下图列出了Havok中布料制作的几个核心概念和它们之间的关系。
这里面Simulation Cloth,以及Buffer都可以被看做承载粒子和弹簧数据的容器,Collidable是用于碰撞的胶囊体,布料属性通过一系列Operator来进行设定,多个Operator组成一个State,多个State又可以重复执行以多次模拟计算的方式获取更好的布料效果。为了避免布料穿插或者提升碰撞检测的效率,Havok又提供了Local Range Constraints、Bone Global Plane Constraints等多种约束来在正常的模拟之外限定布料粒子可以运动的范围,比如Local Range Constraints就以半径、最小距离和最大距离这三个参数指定了粒子可以运动的空间范围。
可见Havok中对于布料系统的设计和实现是如此的复杂。因此,在查阅Unity中布料制作方法的时候我以为会有大量的资料和内容需要学习,结果官方文档中的Cloth部分只有几屏幕内容,而其除了用于处理碰撞之外的核心组件只有Cloth Component一个,并且整个制作过程是在Unity Editor内部的,着实让我有点意外。在学习具体制作方法之前,还是老思路,先对Unity丰富的插件进行一下调研。
5.1 插件调研
需要提前说明的是,前文所描述的布料系统及其原理,主要是针对在主机和端游上使用的物理模型的布料效果,它们基于网格顶点,可以做到非常精细的效果。其实还有一种制作布料效果的方法——基于骨骼进行物理模拟。这种方法的原理更加简单,把骨骼当做一个个通过关节连接在一起的铰链,给予一定的物理参数,来模拟动画之外的动态效果,而最终效果是通过蒙皮反应到模型网格上的。由于骨骼数量通常都比顶点数量少很多,因此这种方法具有更高的运行效率,在手游中使用得也比较多,前面对于《王者荣耀》中妲己的辫子截图,笔者猜想就是使用的类似的方法。
沿着这两个不同的思路,笔者查看了几个相关的插件。
a. 基于骨骼的方案。基于骨骼的方案在Unity的AssetStore上有多个实现,原理都差不多:Dynamic Bone、Swing Bone、PhysicsBone
b. 基于质子-弹簧模型的方案。主要看了两个,一个是官方的Cloth Component,一个是Obi Cloth。
5.2 制作方法
基于骨骼的布料方案中,笔者购买了Dynamic Bone这个插件进行测试和学习;基于质子-弹簧模型的方案中,笔者选择了官方原生的Cloth Component来进行测试和对比。
方法比较简单,新建一个工程,选择一个项目中使用的角色,分别使用这种方式进行布料效果的制作,然后进行效果和效率对比。
Dynamic Bone提供了一个名为“Dynamic Bone”的脚本组件来实现骨骼链的物理模拟,每一条要模拟的骨骼链都需要添加一个动态骨骼的组件,组件中定义了骨骼链的根节点和阻尼、弹性、刚度系数、惰性、重力等等参数。组件截图如下:
Dynamic Bone插件的使用比较简单,进行一定的配置之后,角色的飘带等就可以在角色移动、播放动画、旋转等情况下有了一些惯性感的动态效果。这里列举几点具体使用时的小Tips:
a. 骨骼链不能通过Optimize Game Object进行优化。这个要求很容易理解,如果在C#层无法取得骨骼对应的GameObject,自然无法对其进行修改。
b. 整个Dynamic Bone组件必须放置在Animator所在的外层才能正常起作用,也就是需要在Animtor所在的GameObject的父节点上添加Dynamic Bone组件才可以。由于我们之前Animator是在角色模型GameObject的最外层的,因此这个需求需要对之前的角色模型结构进行一点小的修改。
c. 最终效果依赖于骨骼链的数量。我们对于飘带等使用的骨骼进行了非常强的限制,通常只有1-2根,因此结果也可想而知,飘动时有比较明显的折角现象。
d. 必须单独绑定骨骼的面片才可以做出动态效果。我们有些饰品和短的飘带绑定到了角色的脊柱等核心骨骼上面,这些骨骼是不能被用来做动态骨骼的,否则角色真个身体就扭曲掉了……
Unity官方所带的Cloth组件使用稍微复杂一点,但是也不太难,基本步骤包括:
- 在Mesh Renderer所在的GameObject上添加Cloth组件;
- 点击Edit Constraints按钮在弹出的Cloth Constraints界面中编辑Mesh顶点上的常量,比如最大距离、表面渗透。也可以通过笔刷来刷出渐变的值。
- 在Cloth组件中设置参数和碰撞来提升效果。
具体制作的细节流程这里不详述了,提供一个Youtube上的教学视频,有比较详细的过程——《GAME ASSET TUTORIAL - How to Create a Cloth in Unity 5 (PART 2/2)》。
Cloth组件的效果因为是基于网格的,因此不需要绑定骨骼也可以做出动态的效果,而且飘动的单元是mesh顶点,因此效果也比较细腻。(我们的角色面数也比较省,当然做不到像端游那么精细的效果。)
5.3 方案对比
在前面的制作过程中已经就这两种方案的一些方面进行了简单对比,这里主要是针对性能进行一些对比工作。首先是在电脑上通过Profiler进行查看,Demo中有10个Unity Cloth和10个Dynamic Bone制作的角色布料效果,由于游戏还未正式上线,因此Demo就不截图了,只给出Profiler的截图:
可以看出,即使是在PC上,基于物理的Unity Cloth组件占用消耗也是比较大的,有0.28ms的消耗,当然Dynamic Bone也“不甘示弱”,两个Update加在一起也有0.21ms。在手机上,使用同样的Demo进行测试,笔者使用了三年前的魅族MX4 PRO进行测试,Unity Cloth会从60帧的满帧降低到大约30帧左右,而开启Dynamic Bone只从60帧降低到50+帧,可以看到在手机上的效能消耗还有较大差别,算下来在这台设备上基于物理的Unity Cloth比Dynamic Bone要多消耗10ms左右。
需要说明的是,这里的对比只是一个大概的感性对比,首先我们只会将布料效果应用在选人界面或者角色浏览界面中,而且低配会关闭掉,因此性能上的差异不会作为一个非常重要的考量点,另外基于物理的Cloth组件为了方便制作,笔者并没有把布料的部分单独拆出来,因此整个模型都作为布料粒子来模拟,只是大部分的顶点的Max Distance设置为0。这应该会对内存的占用和性能消耗有一定的影响。
针对目前项目中角色的制作方法和规范,笔者整理了一下两种方案的对比情况如下表:
这里提一个Tips,Unity的Cloth笔者在电脑上测试发现,如果只Deactive Cloth组件或者整个GameObject并不能让PhysX部分的消耗消失,看上去是只能减少一些,需要把角色的GameObject从场景中删除掉才可以。并不确定这是否是一个Unity的bug,从某种程度上这样做也可以理解——毕竟物理效果是需要连续性的,即使模型并不在渲染,也需要进行一些物理模拟来保证模型被激活的时候的正确性。
5.4 方案选择
最终的方案选择还要再和美术一起过一下效果再做决定,从目前来看,鉴于计划只在非常特定的场合下使用,而且我们角色的骨骼数量实在有限,添加骨骼的成本也比较高,Unity的Cloth可能会是我们最终的选择。
6. 总结
本文讲述了基于物理模拟的布料系统的实现原理和应用方法,也记录了最近在Unity中引入布料效果所做的一些工作,希望可以给可能会用到的读者一些帮助,也希望有经验的读者提供一些建议和分享。
最后想借着布料效果这一主题聊一个不太相关的感受。对比笔者曾经熟悉的Havok引擎,Unity引擎的Cloth组件提供了非常简单实用的封装。笔者在学习和整理Havok中的布料系统的制作方法的时候,需要在3DS Max中进行网格的拆分、Buffer的建立、约束和状态的配置和通道参数的设置等等步骤,才可以在最终的游戏中看到效果,而参数的修改和结果预览也是相对麻烦的事情。当然与复杂对应的就是丰富的效果,几乎任何游戏中需要的柔体效果都可以进行实现,从武器长鞭到羞羞的乳摇,从柔软的丝绸到弹弹的果冻,只要工具用得足够熟练,概念理解得足够清晰,美术可以制作乃至创造出各种想象不到的效果。而Unity中的Cloth组件,只提供十几个参数和一个简单的常量笔刷,相对于Havok可谓简陋至极,但也能实现六七成的布料效果,对于美术上手来说非常简便,整个流程大都可以在Editor中进行,而更好更复杂的效果交给插件开发者来实现。这两种引擎设计理念完全不同,一个大而全,一个小而精,也许前者更强大,但对于入门者来说,后者更好用。我想,这也是Unity引擎可以有这么大范围应用的原因之一吧。