符号约定
-
离散时间节点 t1,t2,...,tn,...t_1,t_2,...,t_n,...t1,t2,...,tn,... 和固定时间间隔 h=tn−tn−1h=t_n-t_{n-1}h=tn−tn−1
-
系统节点数目 mmm ,系统弹簧数目 sss
-
系统状态 qn∈R3m\boldsymbol{q}_n\in\mathbb{R}^{3m}qn∈R3m,描述在 tnt_ntn 时刻每一个节点的空间位置
-
质量矩阵 M∈R3m×3m\boldsymbol{M}\in\mathbb{R}^{3m\times 3m}M∈R3m×3m,是一个对角矩阵
-
速度 vn∈R3m\boldsymbol{v}_n\in\mathbb{R}^{3m}vn∈R3m,描述在 tnt_ntn 时刻每一个节点的速度向量
-
外力 f(qn):R3m→R3m\boldsymbol{f}(\boldsymbol{q}_n):\mathbb{R}^{3m}\rightarrow\mathbb{R}^{3m}f(qn):R3m→R3m
-
系统能量 E:R3m→RE:\mathbb{R}^{3m}\rightarrow\mathbb{R}E:R3m→R,有 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+hM−1f(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+1−2qn+qn−1=h2M−1f(qn+1)(3)
物理仿真的目的是根据已知条件求解 qn+1\boldsymbol{q}_{n+1}qn+1,因此我们可以简记 x:=qn+1\boldsymbol{x}:=\boldsymbol{q}_{n+1}x:=qn+1,y:=2qn−qn−1\boldsymbol{y}:=2\boldsymbol{q}_n-\boldsymbol{q}_{n-1}y:=2qn−qn−1,那么方程就变成了
M(x−y)=h2f(x)(4)
\boldsymbol{M}(\boldsymbol{x}-\boldsymbol{y})=h^2\boldsymbol{f}(\boldsymbol{x}) \tag{4}
M(x−y)=h2f(x)(4)
式中 y\boldsymbol{y}y 可以看成是惯性,如果没有外力,那么下一刻系统状态将会变成 qn+(qn−qn−1)\boldsymbol{q}_n+(\boldsymbol{q}_n-\boldsymbol{q}_{n-1})qn+(qn−qn−1)。要注意的是我们平时容易把 y\boldsymbol{y}y 当成函数来看,但这里方程(4)中只有 x\boldsymbol{x}x 和 f(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(x−y)TM(x−y)+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=xn−Hessg(xn)−1∇g(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=1∑ski(∣∣pi1−pi2∣∣−r)2(6)
其中 kik_iki 是弹性系数,pi1\boldsymbol{p}_{i_1}pi1 和 pi2\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)=mind∈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)=d∈Umin21i=1∑ski∣∣pi1−pi2−di∣∣2(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(∣∣p1−p2∣∣−r)2=∣∣d∣∣=rmin∣∣p1−p2−d∣∣2 ,这个很好理解,一个点距离球面最近的点,就是这个点与球心连线(的延长线)和球面的交点。变换成 (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}
∣∣p1−p2−d∣∣2=∣∣p1∣∣2+∣∣p2∣∣2+∣∣d∣∣2−2p1⋅p2−2p1⋅d+2p2⋅d=∣∣p1−p2∣∣2−2(p1−p2)⋅d+r2
由于是优化问题,常数项 r2r^2r2 可以丢弃,因此式中剩下一个很像二次函数的东西,于是就可以得到论文中的华丽变换(论文中这个公式其实不是严格等号,差了一个 r2r^2r2 常数项,加上 min 会好一些):
mind∈U12∑i=1ski∣∣pi1−pi2−di∣∣2=mind∈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}
d∈Umin21i=1∑ski∣∣pi1−pi2−di∣∣2=d∈Umin21xTLx−xTJd(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}L∈R3m×3m,J∈R3m×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=1∑skiAiAiT)⨂I3J=(i=1∑skiAiSiT)⨂I3(9)
其中 Ai∈Rm\boldsymbol{A}_i\in\mathbb{R}^mAi∈Rm 是关于弹簧端点索引 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}^sSi∈Rs 是关于弹簧索引 iii 的向量,有 Si,i=1S_{i,i}=1Si,i=1,其它向量元素都是 0 ;⨂\bigotimes⨂ 是克罗内克积。
如果算上外力 fext∈R3m\boldsymbol{f}_{ext}\in\mathbb{R}^{3m}fext∈R3m,系统能量可以表示为:
E(x)=mind∈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)=d∈Umin21xTLx−xTJd−xTfext(10)
将(10)代入(5)得到最终的优化问题
minx∈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}
x∈R3m,d∈Umin21xT(M+h2L)x−h2xTJd−h2xTfext−21xTMy−21yTMx(11)
式中舍弃了一个常数项 12yTMy\frac{1}{2}\boldsymbol{y}^T\boldsymbol{M}\boldsymbol{y}21yTMy 。
数值计算
论文中描述的求解(11)的方法是块坐标下降法,需要计算若干个迭代,每次迭代有两个步骤:local step 和 global step ,分别说明如下:
-
local step 是固定 x\boldsymbol{x}x 计算 d\boldsymbol{d}d,其实就是把 d\boldsymbol{d}d 投影到弹簧原长的位置,具体操作为遍历每个弹簧,找到其两个端点 pi1\boldsymbol{p}_{i_1}pi1 和 pi2\boldsymbol{p}_{i_2}pi2,记 pi12=pi1−pi2\boldsymbol{p}_{i_{12}}=\boldsymbol{p}_{i_1}-\boldsymbol{p}_{i_2}pi12=pi1−pi2,那么就更新 di=ripi12∣∣pi12∣∣\boldsymbol{d}_i = r_i \frac{\boldsymbol{p}_{i_{12}}}{||\boldsymbol{p}_{i_{12}}||}di=ri∣∣pi12∣∣pi12 。
-
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+α(qn−qn−1) 即可。
demo
我基于 fast mass spring 用OpenGL(GLFW)做了布料仿真。布料的配置过程是这样的:
- 正方形布料,边节点数目为 nnn ,总节点数目为 n2n^2n2 (n>1n>1n>1);
- 布料宽度为 www,相邻节点间隔为 w/(n−1)w/(n-1)w/(n−1);
- 布料弹簧数目为 6n2−10n+26n^2-10n+26n2−10n+2,其中 structural 类型的弹簧 2n(n−1)2n(n-1)2n(n−1) 个,shearing类型的弹簧 2(n−1)22(n-1)^22(n−1)2 个,bending类型的弹簧 2n(n−2)2n(n-2)2n(n−2) 个;
设置悬挂节点质量为无穷大,外力为0,得到如下效果:
![]() | ![]() |
![]() | ![]() |
该算法在 Intel i7-11700K CPU 单核上,3600个节点,2万多个弹簧,每一帧迭代 10次,运行帧率可以超过60帧,非常丝滑~




5536

被折叠的 条评论
为什么被折叠?



