Fast Mass Spring 方法学习与布料仿真

高效物理仿真算法:基于快速质弹簧系统的实现
符号约定
  • 离散时间节点 t1,t2,...,tn,...t_1,t_2,...,t_n,...t1,t2,...,tn,... 和固定时间间隔 h=tn−tn−1h=t_n-t_{n-1}h=tntn1

  • 系统节点数目 mmm ,系统弹簧数目 sss

  • 系统状态 qn∈R3m\boldsymbol{q}_n\in\mathbb{R}^{3m}qnR3m,描述在 tnt_ntn 时刻每一个节点的空间位置

  • 质量矩阵 M∈R3m×3m\boldsymbol{M}\in\mathbb{R}^{3m\times 3m}MR3m×3m,是一个对角矩阵

  • 速度 vn∈R3m\boldsymbol{v}_n\in\mathbb{R}^{3m}vnR3m,描述在 tnt_ntn 时刻每一个节点的速度向量

  • 外力 f(qn):R3m→R3m\boldsymbol{f}(\boldsymbol{q}_n):\mathbb{R}^{3m}\rightarrow\mathbb{R}^{3m}f(qn):R3mR3m

  • 系统能量 E:R3m→RE:\mathbb{R}^{3m}\rightarrow\mathbb{R}E:R3mR,有 f=−∇E\boldsymbol{f}=-\nabla Ef=E


公式推导

根据牛顿第二定律得到关于系统状态的隐式积分
qn+1=qn+hvn+1vn+1=vn+hM−1f(qn+1)(1) \begin{aligned} \boldsymbol{q}_{n+1}&=\boldsymbol{q}_n+h\boldsymbol{v}_{n+1} \\ \boldsymbol{v}_{n+1}&=\boldsymbol{v}_n+h\boldsymbol{M}^{-1}\boldsymbol{f}(\boldsymbol{q}_{n+1}) \end{aligned} \tag{1} qn+1vn+1=qn+hvn+1=vn+hM1f(qn+1)(1)
我们消去 v\boldsymbol{v}v 可以得到
qn+1−2qn+qn−1=h2M−1f(qn+1)(3) \boldsymbol{q}_{n+1}-2\boldsymbol{q}_n+\boldsymbol{q}_{n-1}=h^2\boldsymbol{M}^{-1}\boldsymbol{f}(\boldsymbol{q}_{n+1}) \tag{3} qn+12qn+qn1=h2M1f(qn+1)(3)
物理仿真的目的是根据已知条件求解 qn+1\boldsymbol{q}_{n+1}qn+1,因此我们可以简记 x:=qn+1\boldsymbol{x}:=\boldsymbol{q}_{n+1}x:=qn+1y:=2qn−qn−1\boldsymbol{y}:=2\boldsymbol{q}_n-\boldsymbol{q}_{n-1}y:=2qnqn1,那么方程就变成了
M(x−y)=h2f(x)(4) \boldsymbol{M}(\boldsymbol{x}-\boldsymbol{y})=h^2\boldsymbol{f}(\boldsymbol{x}) \tag{4} M(xy)=h2f(x)(4)
式中 y\boldsymbol{y}y 可以看成是惯性,如果没有外力,那么下一刻系统状态将会变成 qn+(qn−qn−1)\boldsymbol{q}_n+(\boldsymbol{q}_n-\boldsymbol{q}_{n-1})qn+(qnqn1)。要注意的是我们平时容易把 y\boldsymbol{y}y 当成函数来看,但这里方程(4)中只有 x\boldsymbol{x}xf(x)\boldsymbol{f}(\boldsymbol{x})f(x) 是未知的,其它都是已知的常量。

构造辅助函数
g(x)=12(x−y)TM(x−y)+h2E(x)(5) g(\boldsymbol{x})=\frac{1}{2}(\boldsymbol{x}-\boldsymbol{y})^{T}\boldsymbol{M}(\boldsymbol{x}-\boldsymbol{y})+h^2E(\boldsymbol{x}) \tag{5} g(x)=21(xy)TM(xy)+h2E(x)(5)
求解(4)等价于求解(5)的极小值点。

典型的求解非线性函数极值点的方法是牛顿法:
xn+1=xn−Hessg(xn)−1∇g(xn) \boldsymbol{x}_{n+1}=\boldsymbol{x}_n-Hess_g(\boldsymbol{x}_n)^{-1}\nabla g(\boldsymbol{x}_n) xn+1=xnHessg(xn)1g(xn)
但是牛顿法每一次迭代中,海森矩阵都会变化,因此每次迭代都要求一次海森矩阵的逆,非常费时间。

接下来就是该算法的精华所在。

根据胡克定律,系统能量(弹性势能)表示为:
E(x)=12∑i=1ski(∣∣pi1−pi2∣∣−r)2(6) E(\boldsymbol{x})=\frac{1}{2}\sum\limits_{i=1}^{s}k_i(||\boldsymbol{p}_{i_1}-\boldsymbol{p}_{i_2}||-r)^2 \tag{6} E(x)=21i=1ski(pi1pi2r)2(6)
其中 kik_iki 是弹性系数,pi1\boldsymbol{p}_{i_1}pi1pi2\boldsymbol{p}_{i_2}pi2 是弹簧的端点坐标,i1,i2∈{1,2,...,m}i_1,i_2\in\{1,2,...,m\}i1,i2{1,2,...,m}rrr 是弹簧原长。但是这个式子是不好处理的,因此论文中引入辅助变量 di\boldsymbol{d}_idi,将弹性势能等价表示为:
E(x)=min⁡d∈U12∑i=1ski∣∣pi1−pi2−di∣∣2(7) E(\boldsymbol{x})=\min\limits_{\boldsymbol{d}\in U}\frac{1}{2}\sum\limits_{i=1}^sk_i||\boldsymbol{p}_{i_1}-\boldsymbol{p}_{i_2}-\boldsymbol{d}_i||^2 \tag{7} E(x)=dUmin21i=1skipi1pi2di2(7)
其中 U={(d1,...,ds)∈R3s:∣∣di∣∣=ri}U=\{(\boldsymbol{d}_1,...,\boldsymbol{d}_s)\in\mathbb{R}^{3s}:||\boldsymbol{d}_i|| = r_i \}U={(d1,...,ds)R3s:di=ri} (论文里这里有一点纰漏)。

(6)和(7)等价的原因是 (∣∣p1−p2∣∣−r)2=min⁡∣∣d∣∣=r∣∣p1−p2−d∣∣2(||\boldsymbol{p}_1-\boldsymbol{p}_2||-r)^2=\min\limits_{||\boldsymbol{d}||=r}||\boldsymbol{p}_1-\boldsymbol{p}_2-\boldsymbol{d}||^2(p1p2r)2=d=rminp1p2d2 ,这个很好理解,一个点距离球面最近的点,就是这个点与球心连线(的延长线)和球面的交点。变换成 (7) 式之后,论文中直接华丽地把公式改写成了矩阵形式,我这里做一点点简单推导:
∣∣p1−p2−d∣∣2=∣∣p1∣∣2+∣∣p2∣∣2+∣∣d∣∣2−2p1⋅p2−2p1⋅d+2p2⋅d=∣∣p1−p2∣∣2−2(p1−p2)⋅d+r2 \begin{aligned} ||\boldsymbol{p}_1-\boldsymbol{p}_2-\boldsymbol{d}||^2 &= ||\boldsymbol{p}_1||^2 + ||\boldsymbol{p}_2||^2 + ||d||^2 - 2\boldsymbol{p}_1\cdot\boldsymbol{p}_2 - 2\boldsymbol{p}_1\cdot\boldsymbol{d} + 2\boldsymbol{p}_2\cdot\boldsymbol{d} \\ &= ||\boldsymbol{p}_1-\boldsymbol{p}_2||^2 - 2(\boldsymbol{p}_1-\boldsymbol{p}_2)\cdot\boldsymbol{d} + r^2 \end{aligned} p1p2d2=p12+p22+d22p1p22p1d+2p2d=p1p222(p1p2)d+r2
由于是优化问题,常数项 r2r^2r2 可以丢弃,因此式中剩下一个很像二次函数的东西,于是就可以得到论文中的华丽变换(论文中这个公式其实不是严格等号,差了一个 r2r^2r2 常数项,加上 min 会好一些):
min⁡d∈U12∑i=1ski∣∣pi1−pi2−di∣∣2=min⁡d∈U12xTLx−xTJd(8) \min\limits_{\boldsymbol{d}\in U}\frac{1}{2}\sum\limits_{i=1}^sk_i||\boldsymbol{p}_{i_1}-\boldsymbol{p}_{i_2}-\boldsymbol{d}_i||^2 = \min\limits_{\boldsymbol{d}\in U} \frac{1}{2}\boldsymbol{x}^T\boldsymbol{L}\boldsymbol{x}-\boldsymbol{x}^T\boldsymbol{J}\boldsymbol{d} \tag{8} dUmin21i=1skipi1pi2di2=dUmin21xTLxxTJd(8)
其中 x=(p1,...,pm)\boldsymbol{x}=(\boldsymbol{p}_1,...,\boldsymbol{p}_m)x=(p1,...,pm),矩阵 L∈R3m×3m,J∈R3m×3s\boldsymbol{L}\in\mathbb{R}^{3m\times 3m},\boldsymbol{J}\in\mathbb{R}^{3m\times3s}LR3m×3m,JR3m×3s 定义如下:
L=(∑i=1skiAiAiT)⨂I3J=(∑i=1skiAiSiT)⨂I3(9) \boldsymbol{L}=(\sum\limits_{i=1}^s k_i \boldsymbol{A}_i\boldsymbol{A}_i^T) \bigotimes \boldsymbol{I}_3 \\ \boldsymbol{J}=(\sum\limits_{i=1}^s k_i \boldsymbol{A}_i\boldsymbol{S}_i^T) \bigotimes \boldsymbol{I}_3 \tag{9} L=(i=1skiAiAiT)I3J=(i=1skiAiSiT)I3(9)
其中 Ai∈Rm\boldsymbol{A}_i\in\mathbb{R}^mAiRm 是关于弹簧端点索引 i1,i2i_1,i_2i1,i2 的向量,有 Ai,i1=1, Ai,i2=−1A_{i,i_1}=1, \ A_{i,i2}=-1Ai,i1=1, Ai,i2=1,其它向量元素都是 0 ;而 Si∈Rs\boldsymbol{S}_i\in\mathbb{R}^sSiRs 是关于弹簧索引 iii 的向量,有 Si,i=1S_{i,i}=1Si,i=1,其它向量元素都是 0 ;⨂\bigotimes 是克罗内克积。

如果算上外力 fext∈R3m\boldsymbol{f}_{ext}\in\mathbb{R}^{3m}fextR3m,系统能量可以表示为:
E(x)=min⁡d∈U12xTLx−xTJd−xTfext(10) E(\boldsymbol{x})=\min\limits_{\boldsymbol{d}\in U} \frac{1}{2}\boldsymbol{x}^T\boldsymbol{L}\boldsymbol{x}-\boldsymbol{x}^T\boldsymbol{J}\boldsymbol{d} - \boldsymbol{x}^T\boldsymbol{f}_{ext} \tag{10} E(x)=dUmin21xTLxxTJdxTfext(10)
将(10)代入(5)得到最终的优化问题
min⁡x∈R3m,d∈U12xT(M+h2L)x−h2xTJd−h2xTfext−12xTMy−12yTMx(11) \min\limits_{\boldsymbol{x}\in\mathbb{R}^{3m},\boldsymbol{d}\in U} \frac{1}{2}\boldsymbol{x}^T(\boldsymbol{M}+h^2\boldsymbol{L})\boldsymbol{x} - h^2\boldsymbol{x}^T\boldsymbol{J}\boldsymbol{d} - h^2\boldsymbol{x}^T\boldsymbol{f}_{ext} - \frac{1}{2}\boldsymbol{x}^T\boldsymbol{M}\boldsymbol{y} - \frac{1}{2}\boldsymbol{y}^T\boldsymbol{M}\boldsymbol{x} \tag{11} xR3m,dUmin21xT(M+h2L)xh2xTJdh2xTfext21xTMy21yTMx(11)
式中舍弃了一个常数项 12yTMy\frac{1}{2}\boldsymbol{y}^T\boldsymbol{M}\boldsymbol{y}21yTMy


数值计算

论文中描述的求解(11)的方法是块坐标下降法,需要计算若干个迭代,每次迭代有两个步骤:local step 和 global step ,分别说明如下:

  1. local step 是固定 x\boldsymbol{x}x 计算 d\boldsymbol{d}d,其实就是把 d\boldsymbol{d}d 投影到弹簧原长的位置,具体操作为遍历每个弹簧,找到其两个端点 pi1\boldsymbol{p}_{i_1}pi1pi2\boldsymbol{p}_{i_2}pi2,记 pi12=pi1−pi2\boldsymbol{p}_{i_{12}}=\boldsymbol{p}_{i_1}-\boldsymbol{p}_{i_2}pi12=pi1pi2,那么就更新 di=ripi12∣∣pi12∣∣\boldsymbol{d}_i = r_i \frac{\boldsymbol{p}_{i_{12}}}{||\boldsymbol{p}_{i_{12}}||}di=ripi12pi12

  2. global step 是固定 d\boldsymbol{d}d 计算 x\boldsymbol{x}x,也就是求一个凸二次优化问题。M+h2L\boldsymbol{M}+h^2\boldsymbol{L}M+h2L 是一个对称正定矩阵,求解(11)的最小值可以令其梯度等于 0 ,也就是:
    (M+h2L)x=h2Jd+My+h2fext(12) (\boldsymbol{M}+h^2\boldsymbol{L})\boldsymbol{x} = h^2\boldsymbol{J}\boldsymbol{d}+\boldsymbol{My}+h^2\boldsymbol{f}_{ext} \tag{12} (M+h2L)x=h2Jd+My+h2fext(12)
    因此该步骤就等价于求解一个线性方程组。因为 M+h2L\boldsymbol{M}+h^2\boldsymbol{L}M+h2L 一直是不变的,所以可以在初始化的时候先做稀疏矩阵 Cholesky 分解(A=LLTA=LL^TA=LLT),可节省大量时间。

如果考虑速度阻尼 α\alphaα,只需令 y=qn+α(qn−qn−1)\boldsymbol{y}=\boldsymbol{q}_n+\alpha(\boldsymbol{q}_n-\boldsymbol{q}_{n-1})y=qn+α(qnqn1) 即可。


demo

我基于 fast mass spring 用OpenGL(GLFW)做了布料仿真。布料的配置过程是这样的:

  • 正方形布料,边节点数目为 nnn ,总节点数目为 n2n^2n2n>1n>1n>1);
  • 布料宽度为 www,相邻节点间隔为 w/(n−1)w/(n-1)w/(n1)
  • 布料弹簧数目为 6n2−10n+26n^2-10n+26n210n+2,其中 structural 类型的弹簧 2n(n−1)2n(n-1)2n(n1) 个,shearing类型的弹簧 2(n−1)22(n-1)^22(n1)2 个,bending类型的弹簧 2n(n−2)2n(n-2)2n(n2) 个;

设置悬挂节点质量为无穷大,外力为0,得到如下效果:

该算法在 Intel i7-11700K CPU 单核上,3600个节点,2万多个弹簧,每一帧迭代 10次,运行帧率可以超过60帧,非常丝滑~

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值