【深入浅出 Nvidia FleX】(1) Position Based Dynamics

【深入浅出 Nvidia FleX】(1) Position Based Dynamics

一、Nvidia FleX简介

FleX 是一款完全基于GPU的物理引擎,其中所有动态物体都是由粒子构成,可以很容易实现不同物体(刚体,软体,流体,布料等)之间的交互效果。如下图:

在传统的物理引擎中,基本上都是针对每一种动态物体,会有一个独立的解算器(Solver),各种Solver按照一定顺序进行计算,从而得到模拟结果,这样会带来大量冗余工作。而FleX把所有动态物体,都用同一种方式表达出来,这样一来,就只需要一个Solver便可以模拟所有这些物体,并原生支持它们之间的双向交互效果。

FleX 采用粒子来描述所有这些动态物体,也就是说在我们的物理世界中,每一个动态物体都由一组粒子通过某种约束(constraint)连接起来,不同的约束关系可以模拟出不同材质的物体。FleX中主要有如下几种约束类型:

距离约束,用于模拟布料,包括布料的拉伸和弯曲等行为;

形状约束,用于模拟刚体和软体;

密度约束,用于模拟流体;

体积约束,用于模拟充气体;

碰撞约束,计算粒子之间的碰撞反应,比如把粒子从穿透状态解开,计算摩擦力等等。

我们现在提供了多平台的实现,而且D3D11 & D3D12版本和CUDA版本在性能表现上基本一致,同时保留了同一套API,所以使用方式和性能上基本没有差别。同时我们将FleX以插件的形式集成到了UE4中,默认使用的是D3D版本,当然也可以通过修改使用CUDA版本来加速。

相关链接:

NVIDIA FleXdeveloper.nvidia.com图标https://github.com/NVIDIAGameWorks/FleXgithub.com
https://github.com/NvPhysX/UnrealEngine/tree/FleX-4.19.2github.com

二、FleX理论基础PBD

上一篇文章 《电影工业中的流体模拟(七)----PCISPH》 介绍的方法属于基于力的方法。这类方法通过计算内力(比如流体内部的粘性力和压力)和外力(比如重力和碰撞力)的合力,然后根据牛顿第二定律计算出加速度,再根据数值积分求出速度和位置信息。

这篇文章主要介绍一下基于位置的方法Position Based Dynamics (PBD)[1],这也是Nvidia FleX物理引擎的理论基础。与基于力的方法先求解力再根据力的值进行数值积分不同,PBD先构建约束,然后通过对约束进行投影(constraint projection)得出位置信息,并依此更新速度值。

为了对这两个方法有个直观认识,我们来看下面两幅物体碰撞的求解示意图[2]:

如果使用基于力的方法,在检测到碰撞之后,需要计算由于物体穿透导致的碰撞力(使两个发生穿透的物体分开),然后根据该力求解出速度和位置信息。这种方法需要计算三个步骤(1)力(2)速度(3)位置才能最终更新物体位置,这样一来就有明显的反应延迟。另外一个更为明显的问题是,计算碰撞力的时候需要选择一个刚度(stiffness)参数(可以将红色箭头理解为弹簧,需要选择合适的弹性系数以产生碰撞力将物体分开)。而刚度系数很难调,刚度值太小会导致物体穿透明显,而刚度值太大则容易造成整个方程组呈现刚性,也就是说需要很小的步长才能对方程组进行准确的数值求解。( 注:刚度是材料力学中的名词,定义为施力与所产生变形量的比值,表示材料或结构抵抗变形的能力[3]。刚度系数越高,物体越不容易发生形变;刚度系数越低,物体越容易发生形变。)

同样的情况,我们看看PBD是怎么处理的,如下图:

在PBD方法中,当检测到两个物体发生穿透时,直接根据约束修正物体位置,然后更新速度信息。从这幅示意图可以看出PBD提供了更多的控制灵活性,这一点对游戏很重要。

下面来介绍经典PBD算法:

在PBD方法中,动力学物体由 N 个顶点和 M 个约束组成。

顶点 i \in [1,\ldots,N] 的质量为 m_i ,位置为 \mathbf{x}_i ,速度为 \mathbf{v}_i

约束 j \in [1,\ldots,M] 有如下五种特性:

  1. 约束的基数为 n_j ,可以理解为第 j 个约束所影响的顶点数目为 n_j
  2. 约束为值为实数的函数, C_j: R^{3n_j} \rightarrow R
  3. 约束索引值为\{i_1,\ldots i_{n_j}\}i_k \in [1,\ldots N]
  4. 每个约束都有对应的刚度参数 k_j \in [0\ldots 1] 。在PBD中,刚度参数可以理解为约束的强度。
  5. 约束分为等式约束 C_j(\mathbf{x}_{i_1}, \ldots, \mathbf{x}_{i_{n_j}}) = 0 与不等式约束 C_j(\mathbf{x}_{i_1}, \ldots, \mathbf{x}_{i_{n_j}}) \geq 0

PBD算法:

算法思路:

首先,对顶点位置,速度和质量倒数等进行初始化。这里 w_i = 1/m_i 设置为质量倒数,除了可以避免过多的除法操作以外,还可以处理静态物体 w_i = 0 (可以理解为质量无穷大)。我们把所有不能转换为位置约束的力(比如重力)记为 \mathbf{f}_{ext}(\mathbf{x}_i) ,并根据 \mathbf{f}_{ext}(\mathbf{x}_i) 的值进行一次数值积分预测速度 \mathbf{v}_i 。然后添加阻尼(damping),阻尼可以理解为物体在运动中发生能量耗散而导致速度衰减。所以可以在这里直接对速度进行衰减操作模拟阻尼。计算完速度后我们再计算位置的预测值 \mathbf{p}_i 。第(8)行主要是生成碰撞约束。物体会与周围环境发生碰撞,比如布料落在地板上,流水碰上一面墙等等。这些碰撞约束每个时间步都在发生变化。有了内部约束(比如不可压缩流体的密度约束)和外部约束(比如流体不能穿透地板)的数学公式之后,接下来,我们需要对这些约束进行迭代求解,也就是我们这里的第(10)行约束投影步骤,得到校正后的粒子位置。然后,更新粒子位置和速度信息。最后,在(16)行根据摩擦(friction)和恢复(restitution)系数修改速度(如下图所示[2])。这样,一个完整的PBD模拟步就算完成了。


三、约束和解算器

  1. 约束投影

设有一个基数为 n的约束,关联的点为 \mathbf{p}_1,\ldots,\mathbf{p}_n ,约束函数为 C ,刚度系数为 k 。用 p = [\mathbf{p}_1^T,\ldots,\mathbf{p}_n^T]^T 表示这些点的位置组成的矩阵。则该约束可以表示为:

C(\mathbf{p}) = 0

我们的目标是希望找到位移向量 \Delta \mathbf{p} ,使得在位置 \mathbf{p} + \Delta \mathbf{p} 处约束条件依然满足,即:
C(\mathbf{p} + \Delta \mathbf{p}) = 0

对约束函数 C 在其当前解 \mathbf{p} 的邻域内进行线性化操作(一阶泰勒展开):

C(\mathbf{p} + \Delta \mathbf{p}) \approx C(\mathbf{p}) + \nabla_\mathbf{p} C(\mathbf{p}) \cdot \Delta \mathbf{p} = 0 (1)

PBD方法的一个绝妙之处就是将 \Delta \mathbf{p} 的方向限制在约束梯度 \nabla_\mathbf{p} C(\mathbf{p}) 方向。为了更好的理解这一点,参见下图[2]。约束 C 所涉及到的所有粒子的位置会形成一个高维空间,下图为该空间中满足不同约束条件的粒子位置形成的等值面的二维示意图,在这里为等值线(黑色曲线),当粒子位于该等值线上时满足约束条件 C = 0 。当粒子处于黑色点位置时,不满足约束条件,如果我们沿着该点所在的等值线(灰色曲线)移动,此时刚体模态(Rigid body modes)的方向与该等值线相同,新得到的位置仍然在该等值线上,依然不在黑色曲线 C = 0 上,即不满足约束条件。这可以理解为,约束中存在的误差依然没有得到修正。拿两个粒子形成的距离约束举例,就好比同时移动了两个粒子或者该约束绕自身旋转,但是存在的误差并没有得到更正。而且这样一来还会引入ghost force,这里ghost force可以理解为本不该引入系统中的一种外力,导致系统动量不守恒。所以,我们希望该点的位移方向与刚体模态方向垂直,从而保证系统动量守恒,即从黑点指向红点的方向(\nabla C 的方向)。

因此令:

\Delta \mathbf{p} = \lambda \nabla_\mathbf{p} C(\mathbf{p}) (2)

标量 \lambda 可以看做拉格朗日乘子(Lagrange multiplier)。

可以保持动量(Linear momentum)和角动量(Angular momentum)守恒。

由公式(1)和(2)可得:

\Delta \mathbf{p} = -\frac{C(\mathbf{p})}{|\nabla_\mathbf{p} C(\mathbf{p})|^2} \nabla_\mathbf{p} C(\mathbf{p}) (3)

具体到粒子 i ,约束投影后其对应的位移向量

\Delta \mathbf{p}_i = -s\; \nabla_{\mathbf{p}_i} C(\mathbf{p}_1,\ldots,\mathbf{p}_n) (4)

其中,

s =\frac{C(\mathbf{p}_1,\ldots,\mathbf{p}_n)}{\sum_j|\nabla_{\mathbf{p}_j} C(\mathbf{p}_1,\ldots,\mathbf{p}_n)|^2} (5)

我们可以看到 s 的值对于约束 C 作用范围内的所有点都一样。

前面我们考虑所有粒子质量都相同的情况,现在考虑粒子质量不同的情况。粒子 i 的质量为 m_i ,则其质量倒数为 w_i = 1/m_i 。则公式(2)变为:

\Delta \mathbf{p}_i = \lambda w_i \nabla_{\mathbf{p}_i} C(\mathbf{p})

公式(4)变为:

\Delta \mathbf{p}_i = -s\ w_i \nabla_{\mathbf{p}_i} C(\mathbf{p}_1,\ldots,\mathbf{p}_n) (6)

公式(5)变为:

s = \frac{C(\mathbf{p}_1,\ldots,\mathbf{p}_n)}{\sum_j w_j |\nabla_{\mathbf{p}_j} C(\mathbf{p}_1,\ldots,\mathbf{p}_n)|^2} (7)


2. 简单约束举例

我们拿最简单的约束作为例子应用前面讲到的约束投影方法,如下图所示:

这里,距离约束可以表示为 C(\mathbf{p}_1, \mathbf{p}_2) = |\mathbf{p}_1 - \mathbf{p}_2|-d ,校正位移为 \Delta \mathbf{p}_i 。根据约束投影方法,首先对约束函数 C(\mathbf{p}_1, \mathbf{p}_2) 在点 \mathbf{p}_1\mathbf{p}_2 处分别求梯度,得到:

\nabla_{\mathbf{p}_1} C(\mathbf{p}_1, \mathbf{p}_2) = \frac{\mathbf{p}_1 - \mathbf{p}_2}{|\mathbf{p}_1 - \mathbf{p}_2|}

\nabla_{\mathbf{p}_2} C(\mathbf{p}_1, \mathbf{p}_2) = -\frac{\mathbf{p}_1 - \mathbf{p}_2}{|\mathbf{p}_1 - \mathbf{p}_2|}

上面求 \nabla_{\mathbf{p}_1} C(\mathbf{p}_1, \mathbf{p}_2) 可以这么理解: 将 \mathbf{p}_1\mathbf{p}_2 拆分成三个分量求解:

\frac{\partial C(\mathbf{p}_{1x}, \mathbf{p}_{2x})}{\partial \mathbf{p}_{1x}} = \frac{\mathbf{p}_{1x} - \mathbf{p}_{2x}}{|\mathbf{p}_{1x} - \mathbf{p}_{2x}|}

\frac{\partial C(\mathbf{p}_{1y}, \mathbf{p}_{2y})}{\partial \mathbf{p}_{1y}} = \frac{\mathbf{p}_{1y} - \mathbf{p}_{2y}}{|\mathbf{p}_{1y} - \mathbf{p}_{2y}|}

\frac{\partial C(\mathbf{p}_{1z}, \mathbf{p}_{2z})}{\partial \mathbf{p}_{1z}} = \frac{\mathbf{p}_{1z} - \mathbf{p}_{2z}}{|\mathbf{p}_{1z} - \mathbf{p}_{2z}|}

从而

\nabla_{\mathbf{p}_1} C(\mathbf{p}_1, \mathbf{p}_2)   =(\frac{\partial C(\mathbf{p}_{1x}, \mathbf{p}_{2x})}{\partial \mathbf{p}_{1x}},  \frac{\partial C(\mathbf{p}_{1y}, \mathbf{p}_{2y})}{\partial \mathbf{p}_{1y}} , \frac{\partial C(\mathbf{p}_{1z}, \mathbf{p}_{2z})}{\partial \mathbf{p}_{1z}} )

应用公式(7)可得:

s = \frac{C(\mathbf{p}_1,\ldots,\mathbf{p}_n)}{\sum_j w_j |\nabla_{\mathbf{p}_j} C(\mathbf{p}_1,\ldots,\mathbf{p}_n)|^2}

= \frac{|\mathbf{p}_1 - \mathbf{p}_2|-d} {w_1 |\nabla_{\mathbf{p}_1} C(\mathbf{p}_1, \mathbf{p}_2) |^2 + w_2 |\nabla_{\mathbf{p}_2} C(\mathbf{p}_1, \mathbf{p}_2)|^2}

= \frac{|\mathbf{p}_1 - \mathbf{p}_2|-d}{w_1 + w_2}

s 代入公式(6)可得位移

\Delta \mathbf{p}_1 = - \frac{|\mathbf{p}_1 - \mathbf{p}_2|-d}{w_1 + w_2} \ w_1 \nabla_{\mathbf{p}_1} C(\mathbf{p}_1, \mathbf{p}_2)

= -\frac{w_1}{w_1 + w_2}(|\mathbf{p}_1 - \mathbf{p}_2|-d)\frac{\mathbf{p}_1 - \mathbf{p}_2}{|\mathbf{p}_1 - \mathbf{p}_2|}


\Delta \mathbf{p}_2 = +\frac{w_2}{w_1 + w_2}(|\mathbf{p}_1 - \mathbf{p}_2|-d)\frac{\mathbf{p}_1 - \mathbf{p}_2}{|\mathbf{p}_1 - \mathbf{p}_2|}

前面我们提到过每个约束都有对应的刚度系数 k ,这里我们用 k' = 1-(1-k)^{1/n_s} 去乘以 \Delta \mathbf{p} ,这样 n_s 次迭代之后误差为\Delta \mathbf{p}(1-k')^{n_s} = \Delta \mathbf{p} (1-k) ,与刚度系数 k 成线性关系,但与迭代次数 n_s 无关。则下一时间步的位置分别为:

\mathbf{p}^{t+1}_1 = \mathbf{p}^{t}_1 + k' \Delta \mathbf{p}_1

\mathbf{p}^{t+1}_2 = \mathbf{p}^{t}_2 + k'\Delta \mathbf{p}_2


3. Solver

PBD的输入为 M + M_{coll} 个约束和 N 个点的预测位置 \mathbf{p}_1,\ldots,\mathbf{p}_N ,所需要求解的方程组为非线性非对称方程组或不等式组(碰撞约束导致)。Solver的主要任务就是修正预测位置使新得到的校正位置值满足所有约束。而在约束投影过程中,很难找到合适的 \Delta p = [\mathbf{\Delta p}_1^T,\ldots,\mathbf{\Delta p}_n^T]^T 使得所有约束能够同时得到满足,所以我们通常采用迭代的方式依次对约束进行求解。

PBD中可以使用非线性高斯-赛德尔(Non-Linear Gauss-Seidel, NGS)迭代方法。Gauss-Seidel迭代方法[4]只能求解线性方程组,NGS 在依次对约束进行求解的基础上,加入了约束求解这一非线性操作。与雅克比迭代方法(Jacobi method)[5]不同,NGS Solver在一次迭代中对于顶点位置的修正立即被应用到下一个约束求解,这带来的好处就是显著加快了收敛速度。

NGS虽然很稳定也容易实现,但是该方法收敛速度仍然不是特别快,而且取决于约束求解顺序,且不宜并行化。在以后涉及到GPU并行化的时候会介绍更适合并行化的Solver。

4. 其他约束类型

除了距离约束,我们后续还会结合所模拟物体的类型分别介绍其他一些约束,包括:

模拟流体的密度约束(density)和表面张力(Surface Tension)约束。

密度约束
表面张力约束

模拟布料的拉伸/距离约束(Stretching)& 弯曲约束(Bending)& Long Range Attachments约束。

弯曲约束


Long Range Attachments约束

模拟刚体和软体的形状匹配约束(Shape Matching)。

Shape Matching

模拟充气体的体积约束(Volume Conservation)。

体积约束


以及各种碰撞约束:Particle-Particle,Particle-Plane, Particle-Mesh(Triangle/Convex)。

Particle-Particle
Particle-Plane
Particle-Triangle
Particle-Convex



四、总结

PBD采用几何的方式,通过先建立约束再对约束进行投影的方式来直接计算出位置和速度。这里的约束投影可以理解为通过数学公式计算出物体下一个时间步的位置,使之满足给定的约束条件。(如下图的例子[2]) 这里和PCISPH方法一样也是采用了预测-校正的思路。

尽管PBD方法不如基于力的方法精度高,但是PBD方法具有模拟稳定,允许大时间步长以及高度可控性等优点,非常适合用于游戏物理引擎中模拟各种物理特效。


参考文献:

[1] Müller, Matthias, et al. "Position based dynamics." Journal of Visual Communication and Image Representation 18.2 (2007): 109-118.

[2] Müller, Matthias, et al. "Real time physics: class notes." ACM SIGGRAPH 2008 classes. ACM, 2008.

[3] zh.wikipedia.org/wiki/%

[4] Gauss-Seidel method

[5] Jacobi method - Wikipedia

发布于 2018-11-09

文章被以下专栏收录