介绍
基于位置求解。
方法
约束(Constraint)
对于N个粒子,M个约束:
C1(x)=0...CM(x)=0
其中,x=[x1T,...,xNT]T。
非线性高斯—桑德尔求解(The Non-Linear Gauss-Seidel Solver)
单独地求解每个约束C(x)。在一次迭代中,顶点位置的修正立即应用到下一个约束的求解。
给定x,求解Δx,使得C(x+Δx)=0。
作线性近似:
C(x+Δx)≈C(x)+∇C(x)TΔx=0
由动量守恒定律可知Δx的方向为∇C的方向,引入拉格朗日乘数子λ:
Δx=λM−1∇C(x)
其中,M=diag(m1,m2,...,mN)。
解得
λ=−ΔC(x)TM−1ΔC(x)C(x)=−∑imi−1∥∇xiC(x)∥2C(x)
对于粒子i:
Δxi=λmi−1∇xiC(x)
指定约束
Stretching
距离约束:
C(x1,x2)=∥x1−x2∥−d
梯度:
∇C(x)=[∇x1C(x)∇x2C(x)]=[∥x1−x2∥x1−x2−∥x1−x2∥x1−x2]
拉格朗日乘数子:
λ=−m1−1+m2−1∥x1−x2∥−d
位置修正
Δx1Δx2=m1−1+m2−1−m1−1(∥x1−x2∥−d)∥x1−x2∥x1−x2=m1−1+m2−1m2−1(∥x1−x2∥−d)∥x1−x2∥x1−x2
算法

- 第6行:为了连续碰撞检测,需要当前位置和预测位置。
- 第15行:根据摩擦力系数和恢复力系数调整速度。
实现
void Stretching::solveConstraint()
{
RigidBody &rb0 = mPBD.mRigiBodies[mBodyIndexes[0]];
RigidBody &rb1 = mPBD.mRigiBodies[mBodyIndexes[1]];
Vector3f x0x1 = rb0.mBaryCenter - rb1.mBaryCenter;
float x0x1N = x0x1.norm();
float lambda = -(x0x1N - distance) / (rb0.mInvMass + rb1.mInvMass);
Vector3f deltaX0 = rb0.mInvMass * lambda / x0x1N * x0x1;
Vector3f deltaX1 = -rb1.mInvMass * lambda / x0x1N * x0x1;
rb0.mVelocity += deltaX0 / mPBD.dt;
rb1.mVelocity += deltaX1 / mPBD.dt;
rb0.mBaryCenter += deltaX0;
rb1.mBaryCenter += deltaX1;
}
演示

参考
- M. Zwicker and C. Soler, Position-Based Simulation Methods in Computer Graphics, 2015, The Eurographics Association, 10.2312/egt.20151045