OpenGL进阶(十五) - 弹簧质点系统(Mass Spring Systems)简介 一个模拟变形物体最简单额方法就是将其表示为弹簧质点系统(Mass Spring Systems),因为方法简单,这个框架对于学习物理模拟和时间积分的各种技术都比较理想一个弹簧质点包含了一系列由多个弹簧连接起来的质点,这样的系统的物理属性非常直接,模拟程序也很容易编写 但是简单是有代价了:1.物体的行为依赖于弹簧系统的设置方法;2.很难通过调整弹簧系数来得到想要的结果;3.弹簧质点网格不能直接获取体效果 在很多的应用中这些缺点可以忽略,在这种场合下,弹簧质点网格是最好的选择,因为够快够简单 今天首先说明一下Mass Spring Systems的理论基础,然后来实现绳子的real-time模拟物理模型 假设有N个质点,质量为mi,位置为xi,速度为vi , 1
对于阻尼的计算, 大小和速度成正比,并且符合力守恒,则对于一个质点,其受力方程为:模拟在模拟算法中,牛顿第二定律是关键,f = ma在已知质量和外力的情况下,通过 a=f/m 可以得到加速度,将二阶常微分方程写成两个一阶方程:可以得到解析解:初始状态为 v(to) = vo, x(to)=xo.积分将时间t内所有的变化加和,模拟的过程就是从 to 开始不断地计算 x(t) 和 v(t),然后更新质点的位置最简单的数值解法就是利用有限的差分近似微分:其中delta t 是为时间步长,t为帧号,带入之前的公式得:这个就是显式的欧拉解法,下一时刻的状态完全由当前状态决定整个过程的伪代码如下:[cpp] view plaincopy1. // initialization 2. forall particles i 3. initialize xi , vi and mi 4. endfor 5. // simulation loop 6. loop 7. forall particles i 8. fi ← fg + fcoll + ∑ f(xi , vi , x j , v j ) 9. endfor 10. forall particles i 11. vi ← vi + ∆t fi /mi 12. xi ← xi + ∆t vi 13. endfor 14. display the system every nth time 15. endloop fg表示重力,fcoll表示碰撞外力。
显式积分是最简单的积分方法,不过有一个严重的问题 ——它只能在步长很小的时候稳定,这就意味着在两个可视帧中间需要进行多个模拟步骤,不稳定的主要原因是欧拉方法仅仅依赖于当前状态,它假设在整个步长中,外力是个常量 假设一个场景:两个质点和一根弹簧,轻轻拉伸后松手,两个小球向中间靠拢,这时候如果时间步长过大,由于力 在一个步长中大小不变,最终结果可能导致弹簧越弹越开,最后被拉断 更多关于显式与隐式积分,参考显式方法与隐式方法模拟一根绳子 在布料的模拟中,大部分都使用的弹簧质点系统 整个模拟的的过程就和伪代码中的描述的一样环境:Ubuntu12.04 codeblocks10.04 glm9.5 代码清单:Mass类:质点类,表示质点,主要属性是质量,速度,位置mass.h[cpp] view plaincopy1. #ifndef MASS_H 2. #define MASS_H 3. #include 4. #include 5. class Mass 6. { 7. public: 8. Mass(float m); 9. ~Mass(); 10. void applyForce(glm::vec3 force); 11. void init(); 12. void simulate(float dt); 13. void setPos(glm::vec3 p); 14. void setVel(glm::vec3 v); 15. 16. glm::vec3 getPos(); 17. glm::vec3 getVel(); 18. float getM(); 19. protected: 20. private: 21. // The mass value 22. float m; 23. // Position in space 24. glm::vec3 pos; 25. // Velocity 26. glm::vec3 vel; 27. // Force applied on this mass at an instance 28. glm::vec3 force; 29. 30. }; 31. 32. #endif // MASS_H mass.cpp[cpp] view plaincopy1. #include "mass.h" 2. 3. Mass::Mass(float m) 4. { 5. //ctor 6. this->m = m; 7. } 8. 9. Mass::~Mass() 10. { 11. //dtor 12. } 13. 14. void Mass::applyForce(glm::vec3 f) 15. { 16. this->force += f; 17. } 18. 19. void Mass::init() 20. { 21. force.x = 0; 22. force.y = 0; 23. force.z = 0; 24. } 25. 26. void Mass::simulate(float dt) 27. { 28. vel += (force/m) * dt; 29. pos += vel * dt; 30. } 31. 32. glm::vec3 Mass::getPos() 33. { 34. return pos; 35. } 36. 37. void Mass::setPos(glm::vec3 p) 38. { 39. pos = p; 40. } 41. 42. void Mass::setVel(glm::vec3 v) 43. { 44. this->vel = v; 45. } 46. 47. glm::vec3 Mass::getVel() 48. { 49. return this->vel; 50. } 51. 52. 53. float Mass::getM() 54. { 55. return this->m; 56. } Spring类:弹簧类,一个弹簧,连着两个质点,还有弹簧的一些属性。
spring.h[cpp] view plaincopy1. #ifndef SPRING_H 2. #define SPRING_H 3. #include "mass.h" 4. #include 5. class Spring 6. { 7. public: 8. Spring(); 9. Spring(Mass* mass1, Mass* mass2, 10. float springConstant, float springLength, 11. float frictionConstant); 12. ~Spring(); 13. 14. void solve(); 15. 16. protected: 17. private: 18. Mass* mass1; 19. Mass* mass2; 20. 21. float springConstant; 22. float restLength; 23. float frictionConstant; 24. 25. }; 26. 27. #endif // SPRING_H spring.cpp[cpp] view plaincopy1. #include "spring.h" 2. 3. Spring::Spring() 4. { 5. //ctor 6. } 7. 8. Spring::~Spring() 9. { 10. //dtor 11. } 12. 13. Spring::Spring(Mass* mass1, Mass* mass2, 14. float springConstant, float springLength, 15. float frictionConstant) 16. { 17. this->springConstant = springConstant; //set the springConstant 18. this->restLeng。