Physically Based Modeling: Particle System Dynamics(粒子运动)

<script src="http://widgets.amung.us/classic.js" type="text/javascript"></script> <script type="text/javascript"> </script>

Physically Based Modeling: Particle System Dynamics

 2008-11-23

 

 

 

        

          我们身边的所有物体几乎都是由粒子(Particle)所构成的。在计算机动画中,模拟真实的世界时候,不可避免的要模拟这些粒子的运动。比如胶状体果冻就是很典型的例子。在PSP上有一款叫做《LocoRoco》游戏,游戏的主角就是一个像果冻一样的东西,当它被其他物体挤压的时候,能够明显看到它的物理变形,模拟得相当真实。粒子运动不同于刚体运动,粒子运动时,可能会引起物体形状的变化,就像上面说的果冻一样。

         一个粒子可以用它的位置和速度来描述。在描述粒子的时候,它的速度是一个瞬时速度,我们可以用一个向量来表示这个速度。这个速度是由时间t和位置来决定的,因为粒子在不动的时间和位置速度都不相等。我们可以用下面这个微分方程来描述它们之间的关系。

 

/dot{X} = f(x,t)

 

 

这里X(t)表示粒子P在不同时刻的位置,f(x,t)表示粒子P不同时刻,不同位置的速度。

 

 

 

 

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

 

 

 

 X(t+/Delta t) = X(t)+ /Delta t /cdot f(x,t)

 

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

 

X(t+/Delta t) = X(t) + /dot{X} /cdot /Delta t

 

 

 

而泰勒展开式:

 

 f(x)= f(x_{0})+ f'(x_{0})(x-x_{0})+ /frac{f''(x_{0})}{2!}(x-x_{0})^{2}+......+ /frac{f^{(n)}(x_{0})}{n!}(x-x_{0})^{n}

 

 

 

前面已经说过了 /dot{X} = f(x,t)。但是用欧拉方法很不精确,且不稳定,从公式中我们可以看到它对于二阶导数以后的高阶导数等于0的函数可以准确的近似。在欧拉方法中,近似的精确程度取决于时间间隔长短 /Delta t 越小/Delta t,近似得越精确。

 

 

        

 

  上图中可以看到,蓝色是粒子实际运动轨迹,红色的是使用较小 /Delta t来近似的轨迹,黄色的使用较大的 /Delta t来近似的轨迹。当然还有很多方法来近似粒子的运动,这里只用欧拉方法来模拟粒子的运动。

         下面来看看怎样具体的描述粒子的状态。

         我们可以在“相空间”里描述粒子的状态,“相空间”是一个六维空间,我们可以分别

用位置和速度来描述 /begin{bmatrix} X// V /end{bmatrix},和它的导数来描述 /begin{bmatrix} /dot{X}// /dot{V} /end{bmatrix},于是可以得到:/begin{bmatrix} /dot{X}// /dot{V} /end{bmatrix} = /begin{bmatrix} v// /frac{f}{m} /end{bmatrix}

 

因为对速度在求导数可以得到它的加速度,再根据F = ma可以得到上面的结果。于是一个粒子可以描述为:

 

 

 

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

 

 

 

 

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

 

 

 

下面来看看一个系统都有可能受到那些力的影响。

 

重力(Gravity)

 

万物都受到重力的影响,根据公式可以很容易计算出重力的大小,当然它的方向是向下的。

 

 

 f_{grav}=mg

 

在系统中,重力和粒子,粒子系统间的关系可以用下图来表示。

 

 

 

 

从图中可以看出粒子,粒子系统,力之间的关系。一个力包含了很多信息,其中sys表示这个力属于哪个粒子系统,p表示这个力作用于哪些粒子,G表示重力常数,apply_force是计算该力的方法。

 

粘滞拽力(Viscous Drag)

 

         粘滞拽力是粒子间相互作用的力。它的公式是:

 

 f_{drag}=-kv

 

 

 

 

 

 

         整个模型和重力是一样的,只是重力常数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|}

 

 

 

 

这里k_{S}k_{d} 都是阻尼常数。 /Delta x是弹簧被拉伸或压缩后的距离,在这里就是2个粒子间的实际距离,r是弹簧自然长度, /Delta v是弹簧两端粒子的速度差,下面这个图描述了它们的关系。

 

 

 

 

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

 

 

 

 

下面用代码实现一个粒子系统的主要部份。

 

 

  1. //
  2. //关于力的基类,将它定义为虚类是因为可以很方便的继承和修改里面的函数。
  3. //
  4. class GForceObj
  5. {
  6. public:
  7.     GForceObj() {};
  8.     virtual ~GForceObj() {};
  9.     
  10.     virtual void SetGravity(const GVector3 &_Force) {};  
  11.     virtual void SetForce(const GVector3 &_Force) {}; 
  12.     virtual void ApplyForce() = 0;
  13.     
  14.     //友元类
  15.     friend class GParticle;
  16.     friend class GPSystem;
  17. };
  18. //
  19. //派生于力基类的重力
  20. //
  21. class Gravity : public GForceObj
  22. {
  23. public:
  24.     Gravity(const GVector3 &_Force = GVector3(0.0, -9.8, 0.0));
  25.     Gravity(const Gravity ©);
  26.     virtual ~Gravity() {};
  27.     void AddParticle(GParticle *P);
  28.     virtual void SetGravity(const GVector3 &_Force);
  29.     virtual void ApplyForce();
  30.     friend class GParticle;
  31.     friend class GPSystem;
  32. private:
  33.     GVector3 Force;
  34.     vector<GParticle *> Particles;
  35. };
  36. //
  37. //粘滞拽力
  38. //
  39. class GViscosity : public GForceObj
  40. {
  41. public:
  42.     GViscosity(const double &_Kd = 1.0);
  43.     GViscosity(const GViscosity ©);
  44.     virtual ~GViscosity() {};
  45.     void AddParticle(GParticle *P);
  46.     virtual void ApplyForce();
  47. private:
  48.     double Kd; //粘滞常数
  49.     vector<GParticle *> Particles;
  50. };
  51. //
  52. //外力,粒子除了收到重力,粘滞拽力,弹力的影响外,还可以受到外力的影响
  53. //
  54. class GExtForce: public GForceObj
  55. {
  56. public:
  57.     GExtForce(const GVector3 &_Force = GVector3());
  58.     GExtForce(const GExtForce ©);
  59.     virtual ~GExtForce() {};
  60.     void AddParticle(GParticle *P);
  61.     virtual void SetForce(const GVector3 &_Force);
  62.     virtual void ApplyForce();
  63. private:
  64.     GVector3 Force;
  65.     vector<GParticle *> Particles;
  66. };
  67. //
  68. // 关于描述弹力信息的类,一个弹力连接了2个粒子,相当于一个弹簧连接了2个粒子
  69. // 在粒子系统中,我们要通过这个类来设定弹簧的自然长度,和连接的粒子
  70. //
  71. class GSpringInfo
  72. {
  73. public:
  74.     GSpringInfo(){};
  75.     GSpringInfo(GParticle* _P0,GParticle* _P1,double _len);
  76.     
  77. private:
  78.     double InitLen; //弹簧自然长度
  79.     GParticle *P0; //粒子1
  80.     GParticle *P1; //粒子2
  81.     friend class GSpring;
  82. };
  83. //
  84. //弹力
  85. //
  86. class GSpring : public GForceObj
  87. {
  88. public:
  89.     GSpring(const double &_Ks, const double &_Kd);
  90.     GSpring(const GSpring ©);
  91.     virtual ~GSpring(){};
  92.     void AddSpringInfo(GSpringInfo* sif);
  93.     virtual void ApplyForce();
  94. private:
  95.     float Ks,Kd; //阻尼系数
  96.     vector<GSpringInfo *> SpringInfo;
  97. };
  98. //粒子类
  99. class GParticle
  100. {
  101. public:
  102.     //初始化粒子,默认质量为1.0
  103.     GParticle(const double &_Mass = 1.0, const GVector3 &_Pos = GVector3(), const GVector3 &_Vel = GVector3(), const GVector3 &_Force = GVector3(),bool movable = true);
  104.     GParticle(const GParticle ©);
  105.     ~GParticle();
  106.     void SetPos(const double &x, const double &y, const double &z); //设置粒子的位置
  107.     void SetMovable(bool movable); //设置该粒子是否可以移动
  108.     GVector3 GetPos() const;
  109.     friend class GPSystem;
  110.     friend class GForceObj;
  111.     friend class GExtForce;
  112.     friend class Gravity;
  113.     friend class GViscosity;
  114.     friend class GSpring;
  115. private:
  116.     double Mass; //质量
  117.     GVector3 Pos; //位置
  118.     GVector3 Vel; //速度
  119.     GVector3 Force; //力
  120.     bool Movable; //是否可以移动
  121. };
  122. //
  123. //粒子系统
  124. //
  125. class GPSystem
  126. {
  127. public:
  128.     GPSystem();
  129.     GPSystem(const GPSystem ©);
  130.     ~GPSystem();
  131.     
  132.     void ClearForce(); //清空所有的力(设置所有力为0)
  133.     void ComputeForce(); //计算力
  134.     void EulerStep(const double &dt); //这就是上面说的是用欧拉方法来模拟粒子运动
  135.     void AddParticle(GParticle *P);// 添加粒子到粒子系统
  136.     void AddForce(GForceObj *F); //添加力到粒子系统
  137.     int GetNumParticle() const//获取粒子总个数
  138.     int GetNumForce() const//获取力的总个数
  139.     GParticle *GetParticle(int idx); //获取粒子
  140.     GForceObj *GetForce(int idx); //获取力
  141.     friend class GForceObj;
  142.     friend class Gravity;
  143.     friend class GViscosity;
  144.     friend class GExtForce;
  145.     
  146. private:
  147.     double Time; //欧拉方法中的时间步长,越小模拟越精确
  148.     GParticle* pParticle;
  149.     vector<GParticle *> Particles;
  150.     vector<GForceObj *> Forces;
  151. };
  152. GPSystem::~GPSystem()
  153. {
  154.     //删除所有粒子
  155.     vector<GParticle *>::iterator it0;
  156.     for (it0 = Particles.begin(); it0 != Particles.end(); ++it0)
  157.         delete (*it0);
  158.     
  159.     //删除所有力
  160.     vector<GForceObj *>::iterator it1;
  161.     for (it1 = Forces.begin(); it1 != Forces.end(); ++it1)
  162.         delete (*it1);
  163. }
  164. void GPSystem::ClearForce()
  165. {
  166.     int NumPtcle = (int)Particles.size();
  167.     for (int i = 0; i != NumPtcle; ++i)
  168.         Particles[i]->Force.Set(0.0, 0.0, 0.0);
  169. }
  170. void GPSystem::ComputeForce()
  171. {
  172.     int NumForce = (int)Forces.size();
  173.     for (int i = 0; i != NumForce; ++i)
  174.         Forces[i]->ApplyForce();
  175. }
  176. //
  177. //欧拉方法的实现
  178. //
  179. void GPSystem::EulerStep(const double &dt)
  180. {
  181.     int NumPtcle = (int)Particles.size();
  182.     for (int i = 0; i != NumPtcle; ++i)
  183.     {
  184.         GVector3 xDot = Particles[i]->Vel; //xDot表示对位置求导数,得到了粒子的速度。
  185.         GVector3 vDot = Particles[i]->Force / Particles[i]->Mass;// vDot表示对速度求导数,得到了粒子的加速度,加速度就是f/m
  186.         Particles[i]->Pos += dt * xDot; //dt时间后的位置
  187.         Particles[i]->Vel += dt * vDot; //dt时间后的速度
  188.     }
  189. }
  190. //
  191. //重力
  192. //
  193. void Gravity::ApplyForce()
  194. {
  195.     int len = (int)Particles.size();
  196.     for (int i = 0; i < len; ++i)
  197.     {
  198.         GParticle *Ptc = Particles[i];
  199.         if(Ptc->Movable)
  200.             Ptc->Force = Ptc->Force + Force;
  201.     }
  202. }
  203. //
  204. //粘滞拽力
  205. //
  206. void GViscosity::ApplyForce()
  207. {
  208.     int len = (int)Particles.size();
  209.     for (int i = 0; i < len; ++i)
  210.     {
  211.         GParticle *Ptc = Particles[i];
  212.         if(Ptc->Movable)
  213.             Ptc->Force = Ptc->Force - Kd * Ptc->Vel;
  214.     }
  215. }
  216. //
  217. //外力
  218. //
  219. void GExtForce::ApplyForce()
  220. {
  221.     int len = (int)Particles.size();
  222.     for (int i = 0; i < len; ++i)
  223.     {
  224.         GParticle *Ptc = Particles[i];
  225.         if(Ptc->Movable)
  226.             Ptc->Force = Ptc->Force + Force;
  227.     }
  228.     Force.Set(0.0, 0.0, 0.0);
  229. }
  230. //
  231. //弹力
  232. //
  233. void GSpring::ApplyForce()
  234. {
  235.     int len = (int)SpringInfo.size();
  236.     for(int i=0; i<len; ++i)
  237.     {
  238.         GParticle *p0 = SpringInfo[i]->P0;
  239.         GParticle *p1 = SpringInfo[i]->P1;
  240.         GVector3 f0, f1;
  241.         GVector3 l = p0->Pos - p1->Pos;
  242.         GVector3 ldot = p0->Vel - p1->Vel;
  243.         f0 = -(Ks * (norm(l) - SpringInfo[i]->InitLen) + Kd * ldot * l / norm(l)) * l / norm(l);
  244.         f1 = -f0;
  245.         if(p0->Movable) 
  246.             p0->Force =  p0->Force + f0;
  247.         if(p1->Movable)
  248.             p1->Force =  p1->Force + f1;
  249.     }
  250. }

*原创文章,转载请注明出处*

 

下面是用上面方法模拟粒子运动视频地址:


 http://kr.youtube.com/watch?v=V9tUqFxS9H0

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

张赐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值