Physically Based Modeling: Particle System Dynamics
2008-11-23

我们身边的所有物体几乎都是由粒子(Particle)所构成的。在计算机动画中,模拟真实的世界时候,不可避免的要模拟这些粒子的运动。比如胶状体果冻就是很典型的例子。在PSP上有一款叫做《LocoRoco》游戏,游戏的主角就是一个像果冻一样的东西,当它被其他物体挤压的时候,能够明显看到它的物理变形,模拟得相当真实。粒子运动不同于刚体运动,粒子运动时,可能会引起物体形状的变化,就像上面说的果冻一样。
一个粒子可以用它的位置和速度来描述。在描述粒子的时候,它的速度是一个瞬时速度,我们可以用一个向量来表示这个速度。这个速度是由时间t和位置来决定的,因为粒子在不动的时间和位置速度都不相等。我们可以用下面这个微分方程来描述它们之间的关系。

这里X(t)表示粒子P在不同时刻的位置,f(x,t)表示粒子P不同时刻,不同位置的速度。
在上图中表示了一个粒子在某时刻的状态。其实这是一个初始值的问题,如果把粒子在不同的时刻放到不同的地方,那么它的速度是不一样。即使在不同时刻放在同一地方,粒子的速度也是不一样的。粒子是沿着途中蓝色的这条轨迹运动的,在很短的时间内( ),我们可以把粒子的运动轨迹看成是直线的,于是可以通过这种方法来近似模拟粒子的运动。下面用欧拉(Euler)方法来近似。在欧拉方法中,给出下面的等式。

这是一种用线性函数替代非线性函数的方法。从上式中可以看到其实这是泰勒(Taylor)的展开式的切断,因为这个式子可以写成

而泰勒展开式:
前面已经说过了
。但是用欧拉方法很不精确,且不稳定,从公式中我们可以看到它对于二阶导数以后的高阶导数等于0的函数可以准确的近似。在欧拉方法中,近似的精确程度取决于时间间隔长短
。 越小
,近似得越精确。
上图中可以看到,蓝色是粒子实际运动轨迹,红色的是使用较小
来近似的轨迹,黄色的使用较大的
来近似的轨迹。当然还有很多方法来近似粒子的运动,这里只用欧拉方法来模拟粒子的运动。
下面来看看怎样具体的描述粒子的状态。
我们可以在“相空间”里描述粒子的状态,“相空间”是一个六维空间,我们可以分别
用位置和速度来描述 :,和它的导数来描述
,于是可以得到:
因为对速度在求导数可以得到它的加速度,再根据F = ma可以得到上面的结果。于是一个粒子可以描述为:

一个粒子系统由很多粒子所组成,我们可以用下面的图来表示。

当然力也存在在粒子系统中,现在我们把力也添加到粒子系统中。

下面来看看一个系统都有可能受到那些力的影响。
重力(Gravity)
万物都受到重力的影响,根据公式可以很容易计算出重力的大小,当然它的方向是向下的。
在系统中,重力和粒子,粒子系统间的关系可以用下图来表示。

从图中可以看出粒子,粒子系统,力之间的关系。一个力包含了很多信息,其中sys表示这个力属于哪个粒子系统,p表示这个力作用于哪些粒子,G表示重力常数,apply_force是计算该力的方法。
粘滞拽力(Viscous Drag)
粘滞拽力是粒子间相互作用的力。它的公式是:


整个模型和重力是一样的,只是重力常数G变成了粘滞拽力常数K。
阻尼弹力(Damped Spring)
阻尼弹力也是粒子间相互作用的力,它的计算有个比较复杂的公式,如下。
![f_{1}= -/left[ k_{s}(|/Delta x-r|)+ k_{d}/left(/frac{/Delta v /cdot /Delta x }{|/Delta x|}/right) /right] /frac{/Delta x}{|/Delta x|}](http://www.codecogs.com/gif.latex?f_{1}=&space;-/left[&space;k_{s}(|/Delta&space;x-r|)+&space;k_{d}/left(/frac{/Delta&space;v&space;/cdot&space;/Delta&space;x&space;}{|/Delta&space;x|}/right)&space;/right]&space;/frac{/Delta&space;x}{|/Delta&space;x|})
这里和
都是阻尼常数。
是弹簧被拉伸或压缩后的距离,在这里就是2个粒子间的实际距离,r是弹簧自然长度,
是弹簧两端粒子的速度差,下面这个图描述了它们的关系。

同样它和粒子,粒子系统间有下面的关系。

下面用代码实现一个粒子系统的主要部份。
- class GForceObj
- {
- public:
- GForceObj() {};
- virtual ~GForceObj() {};
-
- virtual void SetGravity(const GVector3 &_Force) {};
- virtual void SetForce(const GVector3 &_Force) {};
- virtual void ApplyForce() = 0;
-
-
- friend class GParticle;
- friend class GPSystem;
- };
- class Gravity : public GForceObj
- {
- public:
- Gravity(const GVector3 &_Force = GVector3(0.0, -9.8, 0.0));
- Gravity(const Gravity ©);
- virtual ~Gravity() {};
- void AddParticle(GParticle *P);
- virtual void SetGravity(const GVector3 &_Force);
- virtual void ApplyForce();
- friend class GParticle;
- friend class GPSystem;
- private:
- GVector3 Force;
- vector<GParticle *> Particles;
- };
- class GViscosity : public GForceObj
- {
- public:
- GViscosity(const double &_Kd = 1.0);
- GViscosity(const GViscosity ©);
- virtual ~GViscosity() {};
- void AddParticle(GParticle *P);
- virtual void ApplyForce();
- private:
- double Kd;
- vector<GParticle *> Particles;
- };
- class GExtForce: public GForceObj
- {
- public:
- GExtForce(const GVector3 &_Force = GVector3());
- GExtForce(const GExtForce ©);
- virtual ~GExtForce() {};
- void AddParticle(GParticle *P);
- virtual void SetForce(const GVector3 &_Force);
- virtual void ApplyForce();
- private:
- GVector3 Force;
- vector<GParticle *> Particles;
- };
- class GSpringInfo
- {
- public:
- GSpringInfo(){};
- GSpringInfo(GParticle* _P0,GParticle* _P1,double _len);
-
- private:
- double InitLen;
- GParticle *P0;
- GParticle *P1;
- friend class GSpring;
- };
- class GSpring : public GForceObj
- {
- public:
- GSpring(const double &_Ks, const double &_Kd);
- GSpring(const GSpring ©);
- virtual ~GSpring(){};
- void AddSpringInfo(GSpringInfo* sif);
- virtual void ApplyForce();
- private:
- float Ks,Kd;
- vector<GSpringInfo *> SpringInfo;
- };
- class GParticle
- {
- public:
-
- GParticle(const double &_Mass = 1.0, const GVector3 &_Pos = GVector3(), const GVector3 &_Vel = GVector3(), const GVector3 &_Force = GVector3(),bool movable = true);
- GParticle(const GParticle ©);
- ~GParticle();
- void SetPos(const double &x, const double &y, const double &z);
- void SetMovable(bool movable);
- GVector3 GetPos() const;
- friend class GPSystem;
- friend class GForceObj;
- friend class GExtForce;
- friend class Gravity;
- friend class GViscosity;
- friend class GSpring;
- private:
- double Mass;
- GVector3 Pos;
- GVector3 Vel;
- GVector3 Force;
- bool Movable;
- };
- class GPSystem
- {
- public:
- GPSystem();
- GPSystem(const GPSystem ©);
- ~GPSystem();
-
- void ClearForce();
- void ComputeForce();
- void EulerStep(const double &dt);
- void AddParticle(GParticle *P);
- void AddForce(GForceObj *F);
- int GetNumParticle() const;
- int GetNumForce() const;
- GParticle *GetParticle(int idx);
- GForceObj *GetForce(int idx);
- friend class GForceObj;
- friend class Gravity;
- friend class GViscosity;
- friend class GExtForce;
-
- private:
- double Time;
- GParticle* pParticle;
- vector<GParticle *> Particles;
- vector<GForceObj *> Forces;
- };
- GPSystem::~GPSystem()
- {
-
- vector<GParticle *>::iterator it0;
- for (it0 = Particles.begin(); it0 != Particles.end(); ++it0)
- delete (*it0);
-
-
- vector<GForceObj *>::iterator it1;
- for (it1 = Forces.begin(); it1 != Forces.end(); ++it1)
- delete (*it1);
- }
- void GPSystem::ClearForce()
- {
- int NumPtcle = (int)Particles.size();
- for (int i = 0; i != NumPtcle; ++i)
- Particles[i]->Force.Set(0.0, 0.0, 0.0);
- }
- void GPSystem::ComputeForce()
- {
- int NumForce = (int)Forces.size();
- for (int i = 0; i != NumForce; ++i)
- Forces[i]->ApplyForce();
- }
- void GPSystem::EulerStep(const double &dt)
- {
- int NumPtcle = (int)Particles.size();
- for (int i = 0; i != NumPtcle; ++i)
- {
- GVector3 xDot = Particles[i]->Vel;
- GVector3 vDot = Particles[i]->Force / Particles[i]->Mass;
- Particles[i]->Pos += dt * xDot;
- Particles[i]->Vel += dt * vDot;
- }
- }
- void Gravity::ApplyForce()
- {
- int len = (int)Particles.size();
- for (int i = 0; i < len; ++i)
- {
- GParticle *Ptc = Particles[i];
- if(Ptc->Movable)
- Ptc->Force = Ptc->Force + Force;
- }
- }
- void GViscosity::ApplyForce()
- {
- int len = (int)Particles.size();
- for (int i = 0; i < len; ++i)
- {
- GParticle *Ptc = Particles[i];
- if(Ptc->Movable)
- Ptc->Force = Ptc->Force - Kd * Ptc->Vel;
- }
- }
- void GExtForce::ApplyForce()
- {
- int len = (int)Particles.size();
- for (int i = 0; i < len; ++i)
- {
- GParticle *Ptc = Particles[i];
- if(Ptc->Movable)
- Ptc->Force = Ptc->Force + Force;
- }
- Force.Set(0.0, 0.0, 0.0);
- }
- void GSpring::ApplyForce()
- {
- int len = (int)SpringInfo.size();
- for(int i=0; i<len; ++i)
- {
- GParticle *p0 = SpringInfo[i]->P0;
- GParticle *p1 = SpringInfo[i]->P1;
- GVector3 f0, f1;
- GVector3 l = p0->Pos - p1->Pos;
- GVector3 ldot = p0->Vel - p1->Vel;
- f0 = -(Ks * (norm(l) - SpringInfo[i]->InitLen) + Kd * ldot * l / norm(l)) * l / norm(l);
- f1 = -f0;
- if(p0->Movable)
- p0->Force = p0->Force + f0;
- if(p1->Movable)
- p1->Force = p1->Force + f1;
- }
- }
*原创文章,转载请注明出处*
下面是用上面方法模拟粒子运动视频地址:
http://kr.youtube.com/watch?v=V9tUqFxS9H0