01 - 物理系统的基础理论和算法
本文将对游戏引擎中物理系统的基础理论和算法做一些简单介绍,包括物理对象和形状,刚体动力学,碰撞检测及其解决等内容。
基础概念
物理系统对于游戏的真实感是不可或缺的,有了物理系统,游戏将会拥有:
- 物理模拟:例如神秘海域4中开车过市场那一段,车撞到货物货物会飞起来。
- 动态环境:例如彩虹六号中给墙开洞,动态的环境变化容易启发不同的战术。
- 真实感交互:例如半条命亚历克斯,提供非常真实的交互。
- 艺术感:例如布料模拟,落叶等美术效果都依赖于游戏引擎中的物理系统。
物理系统的基础概念如下:
物理对象和形状
玩家眼中的世界和物理系统眼中的世界是不一样的,物理系统更关注于物理模拟而不是画面。
物理对象
静态Actor
是静态的对象,例如墙等不会动的物体。
动态Actor
是动态的对象,可以受其他物体的影响而移动。
Kinematic Actor
是运动学对象,这类对象可以主动影响其他动态Actor,但自己却不受任何影响,是反物理的,并受Gameplay逻辑影响。不恰当使用这种对象容易引发一些bug。
触发器Trigger
和静态Actor类似,是不会动的。但它没有实体,会检测其他实体进入/离开它时,做出相应反应。
物理形状
如果直接用模型本身去进行物理模拟会很麻烦,于是使用一些常用的形状来模拟这些模型,提高通用性和效率。常用到的物理形状如下:
球体
适合模拟一些球状物体。
胶囊体
应用很广泛,通常用胶囊体去包裹人形物体进行物理模拟。
箱体
通常用它来包裹场景中的物体。
凸包网格
在一些体素化/可破坏场景类型的游戏中,破坏场景会“爆”出一些物体,例如石头等,需要用凸包网格包裹它们来进行物理模拟。
三角网格
一些游戏引擎也开始提供对三角网格的物理模拟支持,但仅限于密闭静态的物体。
高度图
高度图不仅可以用来表达地形,还能让地形拥有物理效果。
需要注意的是,这些包围体仅需近似包裹住物体,且形状越简便越好。
形状的属性
物理形状也拥有许多重要的物理属性:
- 质量或密度:决定物体受力影响的程度。
- 重心:决定物体的稳定性。
- 物理材质:拥有弹性、摩擦力等属性的材质,应用到物体上会影响它的运动。
力和冲量
力
通过力给物体附加加速度,以影响它的运动。常见的力有重力、拉力、摩擦力等。
冲量
可以通过冲量即刻影响物体的速度,例如爆炸。
移动
牛顿第一定律
物体不受力的影响,就做匀速直线运动。
牛顿第二定律
在力的影响下,物体加速度的大小跟作用力成正比,跟物体的质量成反比。
如果力是一个常量,那么速度和位移公式如下:
如果力不是一个常量,那么得用微积分来计算物体的速度和位移:
解析解和数值解
以圆周运动为例,计算解析解较为容易,只需套公式就行;但在游戏世界中,需要靠步长来模拟物体运动,需要我们根据当前步长的位移和线速度去计算出下一个步长的位移和线速度。
显式/前向欧拉法
可以使用前向欧拉法进行近似计算,使用当前步长的力和加速度配合步长计算出下个步长的速度近似值,进而计算出下个步长的位移近似值。
使用该方法计算的结果是不收敛的,且步长越大越明显。
使用该方法的优点是效率高,缺点就是上面说的误差越往后越明显。
隐式/后向欧拉法
后向欧拉法则根据未来的状态去推算当前的状态。
使用该方法计算的结果是稳定的,但效率不怎么高。
半隐式欧拉法
该方法就是显式和隐式欧拉法的折中,对于速度的计算使用当前状态近似,对于位移的计算则使用未来状态近似。
效率高,结果也很稳定,但对于简谐运动等的计算结果有瑕疵,十分适合给物理引擎做模拟。
刚体动力学
在之前的内容中,都假设物体是一个质点,没有体积。但实际上大多数物体都有形状和体积,对于这种物体的物理模拟,需要刚体动力学来计算。
物理量
除了线性值,刚体动力学还有一些角度的物理量,包括朝向,角速度,角加速度,转动惯量,角动量,力矩:
朝向
可用一个旋转矩阵或四元数来描述一个物体的旋转朝向。
角速度
角速度w可通过物体表面一点的线速度v和该点朝向旋转轴方向r经过计算得到。
角加速度
转动惯量
转动惯量描述了刚体的质量分布。
假设两刚体由一个无限细的棍子相连,那么它的转动惯量如下:
角动量
根据角动量守恒,左边质量分布集中,角速度大;右边质量分布分散,角速度小。
力矩
应用—桌球
对于桌球,从不同的位置击球带来的效果都不同。假设球桌表面和桌球没有摩擦力,那么球杆击中桌球时,力可以分解为垂直压力和切向摩擦力,就能计算出冲量和速度了。
碰撞检测
如果要让物体之间相互影响,光有描述它们的物理形状还不够,还需要进行碰撞检测。
碰撞检测主要有两个阶段,初筛阶段和缩窄阶段。前者需要快速找到相交的刚体AABB,从而得知可能发生碰撞的刚体;后者需要更进一步判断刚体之间是否发生碰撞,并生成相关信息。
初筛阶段
在初筛阶段中,需要快速找到相交的刚体AABB,可以使用空间划分(如BVH)、排序扫描等方法。
首先是BVH,使用BVH可以快速找到两个物体的AABB是否发生相交。
当物体在移动时,BVH会动态更新,该方法成熟方便。
然后是排序扫描算法,对于每个轴按包围盒坐标进行排序,检查重合的包围盒,如果每个轴上这两个包围盒均重合,说明这两个物体可能发生碰撞。
如果物体发生移动,只需调整小范围的包围盒位置序列即可。
缩窄阶段
找到发生碰撞的物体后,需要在缩窄阶段了解物体碰撞的详细信息,例如哪几个顶点发生了碰撞等。
常用的方法有基本形状相交测试、GJK算法、分离轴理论等。
基本形状相交测试
如果物体的形状是基本形状,就能用这个方法进行相交测试。
等等。
GJK算法
如果相交的物体是凸包,那么就可以用这个算法,该算法更详细的介绍在这里。
不过在了解该算法之前,需要它了解Minkowski和是什么:
Minkowski和
从简单的开始理解,例如求点A和三角形B的Minkowski和,就相当于让坐标系带整个三角形位移,新原点为A:
接下来看看A是线段的情况,相当于让坐标系带整个三角形位移,范围为线段A的左右端点:
如果A是个三角形,同理,相当于让坐标系带整个三角形B位移,范围为三角形A:
那么凸包之间的和如下:
Minkowski差
类似于和,首先要把被减“数”关于原点对称,然后做一遍求和。
算法原理
由于两个相交的物体,Minkowski差形成的凸包一定包含原点。那么就能用这个特性去判断两物体是否相交。
算法步骤
首先找A的最上端点和B的最下端点做Minkowski减法,那么结果必然是凸包的最顶端。
然后需要根据原点位置决定迭代方向,查看原点是否在单纯形(Simplex,即经迭代形成的多边形)内,不在的话就继续找凸包的支撑点(通过移动点Pa和Pb)。
Ps:判断原点是否在三角形内可以用重心坐标、叉乘等方法。
接下来朝着原点方向做迭代,直到做不了迭代为止。
该算法除了可以判断是否发生碰撞外,还可以评估发生碰撞的程度,如撞了多深。
分离轴理论SAT
SAT理论认为,总是可以找到一个分离两个凸包的轴。如果找不到这根轴,说明两个凸包相交了。
可以通过在轴上找一点P,另一凸包上找一点S,求PS在轴法向量上的投影来判断该轴是否是分离轴。
2D情况
在2D情况下,SAT算法会遍历A的边B的顶点,B的边A的顶点,直到找到可以分开AB的轴。
它还有优化算法,就是利用上边提到的求投影来判断是否是分离轴,并暂存最新的分离轴方便后续计算。
3D情况
分离轴变成了分离面,需要进行三次检查步骤:
碰撞解决
发生碰撞了就要解决它,有如下三种方法:
添加斥力
可以粗暴地添加一个和碰撞方向相反的力,让二者互斥弹走。这是早期物理引擎中常用的解法,但现在很少用了。
求解约束
拉格朗日将碰撞解决变成可以通过数学方式求解的约束,对计算机更加友好,通过不断迭代计算可以得到近似解。
求解速度约束
有下图左边三种方法求解速度约束:
场景查询
光线投射RayCast
主动射出一根光线,常常用于弹道模拟等。
分为三种:
- Multiple Hits:查询所有击中点
- Closest Hits:查询最近的击中点
- Any Hits:有击中点就行
其中Any Hits最省资源。
横扫Sweep
和RayCast类似,不过投射的是一个包围体。
覆盖Overlap
要想查询一片区域内有多少物体被影响,可以使用覆盖来快速查询。
碰撞组
还可以给场景中的物体进行分组,以快速查询到可以碰撞到的物体。
效率、准度和确定性
模拟优化策略
Island
可以将场景分成许多Island,分开计算物理模拟。
Sleeping
如果有的Island已经趋于稳定,不需要额外计算,就可以让它进入Sleep状态,直到受到外力才开始计算。
持续碰撞检测CCD
有时候会碰到如下情形,在碰撞检测的两帧内,物体由于速度过快造成“穿墙”的假象,逃过了碰撞检测。这叫做隧穿效应(Tunneling)。
可以让障碍物变得厚一些:
也可以用专门的CCD方法,估算一个两帧间不会发生隧穿效应的安全距离,平时这样计算。然后当物体和障碍物距离小于安全距离时,开始进行频率更快的碰撞检测。
确定性模拟
由于联机游戏要进行同步,因此需要确保不同玩家在同样的输入下,物理模拟的最终结果也同样是确定的。此外,在帧率不同的机器上跑物理模拟,使用相同的输入,结果却不一样,影响玩家的体验。
我们希望在各平台物理模拟的效果是,同样的旧状态+同样的输入=同样的新状态。这需要在各平台上进行固定步数的物理模拟,确定的模拟求解顺序,浮点数要尽量一致。
参考资料
- GAMES104 (boomingtech.com)