在Alan Wake游戏中使用布料的秘密


[补救动画设计师Henrik Enquist描述了他的团队如何制作令人惊叹的花呢夹克仿真恐怖惊悚片《艾伦·威克》的主角。

我们动作惊悚片的主要角色是艾伦·韦克(Alan Wake),他陷入了噩梦,在那儿他被迫与黑暗势力战斗,解决了妻子失踪的奥秘。 他不是训练有素的动作英雄,而是一个普通人。

为了强调角色,我们的艺术总监想穿着一件旧的粗花呢外套给他穿上,外套上肘部有补丁。 游戏是在现实世界中进行的,因此,与幻想游戏或太空射击游戏不同,角色在使用的工具中受到限制。 这意味着我们角色的服装变得更加重要。

为了传达惊悚气氛的幻觉,艾伦·威克(Alan Wake)的夹克应该尽可能地可信。 外套应在风中飘动,并在穿过森林时为角色增加辅助动作。 作为程序员,我立即开始考虑使用组织仿真。

在我们之前的许多游戏中都使用过织物模拟,但是在那里经常使用的技术给人以丝绸或橡胶的感觉-这些材料不适合我们。 直到最近才出现了第三方公司非常出色的组织仿真系统,但是当我们需要稳定的解决方案时,此类工具尚不存在,或者它们无法满足我们的需求。

在本文中,我将讨论我们必须面对的问题以及创建自己的组织模拟的解决方案。

夹克钻机


夹克与角色的其余部分一起建模,就像规则的蒙皮网格一样。 控制外套网格的骨骼是常规骨骼之上的单独层。 夹克袖子使用肩膀和前臂的常规图案。 肩膀和前臂分为一根主骨和一根弯曲骨。 外套的上部由外观约束控制,下部由Verlet模拟控制。


图1.常规游戏骨架顶部的装备外套。

夹克上衣


夹克的骨骼具有从上到下的层次结构(下部的是上部的子代),因此,当上部的骨骼移动时,下部的骨骼会跟随它们。 我们很想将小骨头的女儿直接放在胸部,但是当角色抬起肩膀时,这会导致动作减少,尤其是垂直动作。

在夹克的上半部分,我们模拟了垫在肩膀上的运动,借助查看约束将肩膀的骨骼移向肩膀的骨骼。 因此,垫子紧贴肩膀,举起手时,垫子会像真正的外套一样提起其余的骨头。

查看成本约束约束是什么样的
图片

视线应用于红色锥体

链中的下一个骨骼是夹克的上部和模拟的下部之间的层。 这些骨骼直接由向下看的约束驱动,以补偿肩膀造成的旋转。 我们还添加了左右骨骼之间的位置限制,以补偿肩垫移动时发生的拉伸。


图2.举手时骨骼的运动。

这可能足以实现动画导出器中的限制并烘焙动画数据中的结果,但是我们仍然想实时控制游戏引擎中的骨骼。

因此,我们可以在动画数据中节省一些字节,并且可以轻松地在角色之间传输动画,而不管它们上是否有夹克。 此外,在解决实时约束时,由游戏逆运动学(例如,瞄准时)产生的肩膀运动将正确应用。

外套的底部


解决了夹克上部的问题后,我们开始模拟下部。 大多数的织物游戏模拟在组织模拟的顶点与渲染的网格的顶点之间使用一对一绑定。

我们希望保持外套网格的准确性,以免干扰程序员定义的任何限制。 例如,如果我们决定使用与渲染相同的网格进行织物模拟,则口袋和夹克前部的轮廓将丢失。

可以使用法线贴图来增加夹克的体积,但我们认为这还不够。 我们希望我们的艺术家以他们想要的方式为夹克建模,然后让他们使用法线贴图添加折痕或其他细节,而不是补偿丢失的几何形状。

我们做出了这个决定:创建一个低分辨率织物网格以模拟夹克,然后将其附加到用于控制蒙皮网格的骨骼的骨骼上。



图3.具有相同顶点的夹克和织物的轮廓与仿真的比较。

物理世界


首先,我们研究Verlet的物理原理,然后学习如何为骨骼模拟创建匹配项。 VerléPhysics当前是模拟游戏中面料的标准解决方案。 如果您不熟悉Verlet技术,那么对于初学者来说,我建议阅读以下有关Gamasutra的文章之一: 穿蓝衣服的恶魔:实时布料动画高级角色物理


图4.一个4x4顶点的网格和其中一个顶点的约束。

对于其余的内容,我将简要重复一下工作原理。 图4显示了其顶点之一的织物网格和弹簧约束。 从图中可以看到,每个网格顶点都连接到所有相邻的顶点以及它们的相邻顶点。

来自直接邻居的约束称为拉伸约束,并用蓝色表示。 用红色表示的长约束称为剪切/弯曲约束。

将这些限制存储在两组中非常重要,因为稍后我们将使用不同的参数来解决它们。 请注意,在我们的夹克中,布料的最上面一排是通过蒙皮与角色绑在一起的,不会受到模拟的控制。

网格的存在并不是算法本身的要求,但是,要模拟具有这种拓扑的结构,最容易使用。 组织模拟的基础包括两个部分。 第一部分是Verlet积分,其中我们计算每个顶点的速度并将其应用于位置。

Vector3 vVelocity = vertex.vCurrentPosition - vertex.vPreviousPosition; vertex.vPreviousPosition = vertex.vCurrentPosition; vertex.vCurrentPosition += vVelocity * ( 1.0f - fDampingFactor ) + vAcceleration * fDeltaTime * fDeltaTime; 

在我们的项目中, vAcceleration由重力和风的总和设置。 衰减用于调整夹克的外观并稳定模拟。 高阻尼系数fDampingFactor使夹克感觉很轻,织物会缓慢而顺滑地落下,而低阻尼系数会使夹克较重,从而使夹克在运动后摇摆/摆动的时间更长。

该算法的第二部分是弹簧约束的分辨率(此过程称为松弛)。 对于每个约束,我们彼此吸引或排斥顶点,以使它们满足其原始长度。 这是可读的代码段。

 Vector3 vDelta = constraint.m_vertex1.m_vCurPos - constraint.m_vertex0.m_vCurPos; float fLength = vDelta.length(); vDelta.normalize(); Vector3 vOffset = vDelta * ( fLength - constraint.m_fRestLength ); constraint.m_vertex0.m_vCurrentPosition += vOffset / 2.0f; constraint.m_vertex1.m_vCurrentPosition -= vOffset / 2.0f; 

拉伸约束将织物的顶部固定在一起,倾斜/弯曲约束有助于维持织物的形状。 如您所见,采用该系统的理想解决方案,结构将很难移动。 这就是为什么在解析新位置之前,我们会向倾斜/弯曲限制添加一个系数。

 vOffset *= fStiffness; constraint.m_vertex0.m_vCurrentPosition += vOffset / 2.0f; constraint.m_vertex1.m_vCurrentPosition -= vOffset / 2.0f; 

刚度系数为1.0时,织物将是非柔韧性的;在0.0时,织物将弯曲而没有任何限制。

固定时间步


您必须已经注意到,Verlet集成表明上一个时间步与当前时间步完全相同。 否则,计算出的速度将不正确。 使用Verlet积分时,可以省去可变的时间步长,但是约束的分辨率对时间步长的变化非常敏感。

由于求解器通过迭代规避这些限制来解决问题,因此永远不可能理想地解决它们。 在游戏中,这种误差会表现为延伸,时间步长越短,玩家看到的延伸就越少。

最终,这将是精度与您可以花在衣服上的处理器时间之间的折衷。 如果时间步长不是恒定的,那么衣服的伸展度就会变化,并且我们会将不必要的振动引入系统。 更重要的是,时间步长会影响硬度指标和其他织物参数:时间步长越短,即使使用相同的硬度系数,织物的硬度也会越高。

实际上,这意味着在开始借助面料参数自定义衣服外观之前,您自己必须确定一个固定的时间步长。 我知道有些游戏中物理时间采用可变的时间步长,但是我的亲身经历告诉我,当物理时间和游戏逻辑的时间步长固定时,生活变得更加轻松。

引擎盖


在深入探讨组织模拟的细节之前,让我们快速了解一下引擎盖是如何模拟的。 为了使引擎盖网的顶部蒙皮,我们使用了额外的骨头。 我们创建了一个从骨骼中心到引擎盖后面的位置的摆锤。 摆的末端是一个由Verlet物理学控制的粒子。 然后,使用视线约束,将骨骼指向钟摆。


图5.引擎盖和摆锤。

创建骨骼矩阵


引擎盖为我们提供了一个提示,以了解下一步如何处理夹克的底部。 我们将使用模拟网格中的顶点位置来计算骨骼转换。

我们要做的第一件事是绘制骨骼,使每个骨骼的铰链与模拟网格的顶部匹配。 因此,矩阵中与位移有关的部分的任务将是一个微不足道的过程。

然后,我们需要计算3x3旋转矩阵。 每行(或一列,取决于矩阵的配置)由骨骼的x,y和z轴定义。

我们将骨骼的x轴定义为从基本顶点到其下一个顶点的方向。 然后,y轴由从左侧顶点到右侧顶点的向量定义。


图6.附着在织物网格上的骨头。

在图6中,x轴显示为红色,y轴显示为绿色。 然后,将z轴计算为这些向量的向量积。 最后,我们还对矩阵进行正态化以消除位移数据中的失真。

如您所见,在垂直方向上,我们使用织物网格的每一行(最后一个除外)来调整骨骼,但是在水平方向上,仅每隔两列使用一次。 除了它具有上述的艺术优势外,这种方法也非常快捷。 因此,可以在GPU端使用传统的蒙皮技术来渲染网格,因为否则我们将不得不更新巨大的动态顶点缓冲区。

织物网格的分辨率可能会很低,从而降低了CPU的负载。 我们解决方案的唯一额外成本是将低分辨率模拟转换为高分辨率网格,但是在我们的方案中,与其余模拟相比,这些成本可以忽略不计。

碰撞


为了解决用腿和身体修剪组织的问题,我们使用了椭球和粒子之间的碰撞识别。 图7显示了通过角色模型解决夹克截断所需的椭圆体。


图7. Wake模型的椭球系统。

椭球与粒子碰撞的识别非常快。 可以通过变换椭球和粒子存在的空间来解决碰撞,从而使椭球变成球体。 然后,您可以对球体和粒子执行快速碰撞测试。

实际上,这伴随着基于椭圆体的长度,宽度和高度的值的逆变换的创建,并将其应用于粒子的位置。 唯一的问题是,在转换回原始坐标系后,我们得到的法向碰撞会变形。

我们决定在计算碰撞方向时可能会有些许误差。 如果强力拉伸的椭球体可能引起错误的反应,我们将其分为两个同构的椭球体。

到粒子的最大距离


需要解决的另一个问题是夹克的稳定性。 快速运动过程中的组织可能导致结点的产生或出现在碰撞体积的另一侧并穿过人体。 我们通过为模拟组织的每个顶点设置安全距离来解决此问题。

对于每个顶点,通过剥皮的初始静止位置将附加到最近的骨骼上,我们将其用作参考点。 如果模拟超过阈值,那么我们只需将顶点移近参考点即可。 在我们的设计中,我们允许下方的山峰移动的距离比靠近肩膀的山峰移动的距离更大。

我们可以让峰移动的最大距离约为40厘米,当超过此值时,极少出现节点和截断的情况。 我们也尝试使用其他技术,例如碰撞平面,但是最大距离方法却是最好的。 它快速,容易设置,并提供了最大的移动自由度,可在面料上开始出现明显的错误之前。

花呢更多,橡胶更少


到目前为止,我们已经找到了实现目标的好方法。 我们的艺术家以他喜欢的方式为夹克建模。 对于动画夹克,不需要动画师,因为一切都在游戏中进行了模拟,并且处理器很高兴我们有足够的资源来进行其他游戏中的计算。 但是有一件事困扰着我们-织物看起来像橡胶。

对抗伸展运动


首先,我们需要摆脱困境。 就像我上面说的那样,拉伸现象是由于算法的迭代性质而出现的错误引起的。 这是一个热门的研究主题,可以找到许多方法来解决此问题。

不幸的是,所有可用的解决方案将迫使我们分配更多的稀缺CPU资源用于组织计算。 因此,我们通过在组织模拟中添加最后一步来解决拉伸问题,其中应用了所谓的“硬约束”。

我们对拉伸限制进行了严格限制(它们都垂直定向)。 这些限制从上到下进行排序,以便将肩膀附近的限制解析为腿部附近的限制。

因为我们从上面迭代了约束,所以我们知道该对中的顶部顶点已经解决,并且不会引起任何拉伸,因此我们只需要将底部顶点移向顶部即可。 因此,我们可以确保在一次迭代之后,从顶部到底部的长度将与静止时的长度完全相同。

 Vector3 vDelta = constraint.m_vertexTop.m_vCurPos - constraint.m_vertexDown.m_vCurPos; float fLength = vDelta.length(); vDelta.normalize(); Vector3 vOffset = vDelta * ( fLength - constraint.m_fRestLength ); constraint.m_vertexDown.m_vCurrentPosition += vOffset; 


图8.严格的限制。

如您所见,我们没有考虑夹克的水平拉伸。 不可能对水平方向施加严格的限制,因为在这种情况下,顶点将被解析两次,也就是说,我们将失去垂直计算阶段的结果,并且织物的长度将无法保存。

但是,我们注意到,在夹克的情况下,水平拉伸实际上对人眼是不可见的,并且由于垂直拉伸,夹克看起来非常糟糕。 事实证明该解决方案非常好。

外套边缘


其次,我们希望夹克的边缘比其余部分移动更多。 例如,如果您穿着敞开的外套,您会发现空气阻力对外套边缘的影响比对中央部分的影响更大。 这是因为您的身体被风遮住了外套的其余部分。

可以通过附加到它们的约束的数量轻松找到边缘。 具有少于四个拉伸约束的任何顶点都是边。 因此,我们可以标记这些顶点并使用其他参数对其进行仿真。

  • 减少衰减。
  • 全球风影响更大。
  • 世界空间中的运动具有更大的影响力(更多关于世界空间中的运动,请参见下文)。
  • 最大允许安全距离更高。

因此,边缘的内部频率将不同于夹克的其余部分。 现在整个外套不再像大摆一样对冲动做出反应,只有边缘为机芯增加了优美的辅助动作。


图9.边缘的顶部。

世界空间和局部空间的运动


然后我们注意到,当移动角色时,世界空间中的移动会对模拟产生相当大的影响,而较小的局部身体转弯或肩膀移动就不会引起注意。

在传统的组织模拟中,在世界空间中模拟顶点的位置。 有人可能会说模拟织物是正确的,但是感觉很不自然。 因此,我们在本地空间中的角色上模拟了夹克,并在世界空间中单独添加了一些动作。 我们注意到,我们所需的结果是通过骨骼的100%局部动画以及在世界空间中移动10-30%来获得的。

摩擦力


最后,我们想夸大夹克在慢速运动和快速运动之间的对比。 我们希望夹克在行走时相对静止不动,而当Alan跳跃或躲闪时,运动应该更加生动。

我们认为,夹克与身体接触时,由于夹克与衬衫之间的摩擦,其移动应减少;当夹克升起时,由于没有任何限制,它应移动得更多。 我们通过向与椭球接触的每个顶点应用增加的衰减值来模拟此情况。 因此,触摸身体的顶部会显得有些粘腻,在正常情况下和快速移动时在夹克之间形成足够的对比度。

结论和进一步的工作


组织模拟的第一个实施方案实施起来非常简单:我们只是在游戏开发文献中搜索“ fabric”一词,然后应用发现的算法。 第二阶段,我们试图使花呢夹克产生令人信服的感觉,这需要研究科学文章,许多试验和错误,甚至是删除部分代码。

当然,您可以随时改进。 例如,使用低分辨率仿真并将其链接到高分辨率网格,会使所有截断问题的解决方案变得复杂。 我们没有足够的时间来处理其他小细节:例如,这些是夹克折痕处的折卡,或者是夹克与龙卷风之间正确交互的实现方式。

最终,我们的努力得到了回报-我们的面料与其他游戏中的组织仿真非常不同。 她看起来更像是粗花呢,而不是丝绸或橡胶。 另外,我们的系统非常灵活,可以模拟其他织物,例如Barry Wheeler的羽绒服和老太太的面纱。 似乎通过调整参数可以实现模拟和其他类型的组织。


图10.花呢外套。

Source: https://habr.com/ru/post/zh-CN458486/


All Articles