• GAMES101-ASSIGNMENT8(作业8)


    1 总览

    1.1 连接绳子的约束

            在 rope.cpp 中, 实现 Rope 类的构造函数。这个构造函数应该可以创建一个新的绳子 (Rope) 对象,该对象从 start 开始, end 结束,包含 num_nodes 个节点。
    也就是如下图所示:


            每个结点都有质量,称为质点;质点之间的线段是一个弹簧。通过创建一系列的质点和弹簧,你就可以创建一个像弹簧一样运动的物体。

            pinned_nodes 设置结点的索引。这些索引对应结点的固定属性 (pinned attribute) 应该设置为真(他们是静止的)。对于每一个结点,你应该构造一个 Mass对象,并在 Mass 对象的构造函数里设置质量和固定属性。(请仔细阅读代码,确定传递给构造函数的参数)。你应该在连续的两个结点之间创建一个弹簧,设置弹簧两端的结点索引和弹簧系数 k,请检查构造函数的签名以确定传入的参数。

            运行./ropesim。你应该可以看到屏幕上画出绳子,但它不发生运动。

     1.2 显式/半隐式欧拉法

             胡克定律表示弹簧连接的两个质点之间的力和他们之间的距离成比例。也就是:

     

            在 Rope::simulateEuler 中, 首先实现胡克定律。遍历所有的弹簧,对弹簧两端的质点施加正确的弹簧力。保证力的方向是正确的!对每个质点,累加所有的弹簧力。

            一旦计算出所有的弹簧力,对每个质点应用物理定律:

     

            运行./ropesim。仿真应该就开始运行了,但是只有 3 个结点,看起来不够多。在application.cpp 文件的最上方,你应该可以看到欧拉绳子和 Verlet 绳子的定义。改变两个绳子结点个数(默认为 3 个),比如 16 或者更多。

            运行 ./ropesim -s 32 来设置仿真中每帧不同的仿真步数。尝试设置较小的值和较大的值(默认值为 64)。

     1.3 显式 Verlet

            Verlet 是另一种精确求解所有约束的方法。这种方法的优点是只处理仿真中顶点的位置并且保证四阶精度。和欧拉法不同, Verlet 积分按如下的方式来更新下一步位置:

            除此之外,我们可以仿真弹簧系数无限大的弹簧。不用再考虑弹簧力,而是用解约束的方法来更新质点位置:只要简单的移动每个质点的位置使得弹簧的长度保持原长。修正向量应该和两个质点之间的位移成比例,方向为一个质点指向另一质点。每个质点应该移动位移的一半。只要对每个弹簧执行这样的操作,我们就可以得到稳定的仿真。为了使运动更加平滑,每一帧可能需要更多的仿真次数。

    1.4 阻尼

            向显示 Verlet 方法积分的胡克定律中加入阻尼。现实中的弹簧不会永远跳动-因为动能会因摩擦而减小。阻尼系数设置为 0.00005, 加入阻尼之后质点位置更新如下:

     你应该修改的函数是:
    • rope.cpp 中的 Rope::rope(...)
    • rope.cpp 中的 void Rope::simulateEuler(...)
    • rope.cpp 中的 void Rope::simulateVerlet(...)

     2 开始编写

     2.0.1 依赖

    本次作业需要预先安装 OpenGL, Freetype 还有 RandR 这三个库。可以通以下命令进行安装:

    sudo apt install libglu1 -mesa-dev freeglut3 -dev \\mesa-common-dev

    sudo apt install xorg-dev #会自动安装libfreetype6 -dev

     请下载工程的代码框架并通过下面的命令创建工程:
    $ mkdir b u i l d
    $ cd b u i l d
    $ cmake . .
    $ make
    之后,你应该可以使用命令./ropesim 来运行仿真

     3 代码

     Rope

    1. Rope::Rope(Vector2D start, Vector2D end, int num_nodes, float node_mass, float k, vector<int> pinned_nodes)
    2. {
    3. // TODO (Part 1): Create a rope starting at `start`, ending at `end`, and containing `num_nodes` nodes.
    4. //计算节点间的步长
    5. Vector2D step = (end-start)/(num_nodes-1);
    6. for (int i = 0 ;i < num_nodes; i++){
    7. //设置每个结点的质量和固定属性
    8. Mass* mass = new Mass (start + step * i,node_mass,false);
    9. //结点的速度
    10. mass->velocity = Vector2D(0,0);
    11. //遍历每个结点,将每两个结点间的绳子当做弹簧属性来保存,通过一些列的质点和弹簧就能创建一个弹簧一样运动的物体
    12. if (i > 0){
    13. //每两个结点创建一个新的弹簧
    14. Spring* spring = new Spring(masses.back(),mass,k);
    15. //将这段弹簧放到绳子的springs栈中
    16. springs.push_back(spring);
    17. }
    18. masses.push_back(mass);
    19. }
    20. // Comment-in this part when you implement the constructor
    21. for (auto &i : pinned_nodes) {
    22. masses[i]->pinned = true;
    23. }
    24. }

     simulateEuler

    1. void Rope::simulateEuler(float delta_t, Vector2D gravity)
    2. {
    3. for (auto &s : springs)
    4. {
    5. // TODO (Part 2): Use Hooke's law to calculate the force on a node
    6. //使用胡克定律计算质点所受的力
    7. Vector2D ab = (s->m2) -> position - (s ->m1)->position;
    8. Vector2D f = s -> k*ab.unit()*(ab.norm() - s->rest_length);
    9. //注意方向,弹簧的两个质点的方向相反
    10. s->m1->forces -= f;
    11. s->m2->forces += f;
    12. }
    13. for (auto &m : masses)
    14. {
    15. if (!m->pinned)
    16. {
    17. // TODO (Part 2): Add the force due to gravity, then compute the new velocity and position
    18. //加上重力对质点的影响
    19. m->forces += gravity*m->mass;
    20. float k_d = 0.01f;
    21. Vector2D f_d = -k_d * m->velocity;
    22. m->forces += f_d;
    23. //a = f/m
    24. Vector2D a = m->forces/m->mass;
    25. // TODO (Part 2): Add global damping
    26. // v = a * t,
    27. m->velocity += a*delta_t;
    28. // l = v * t ; p' = p + l
    29. m->position += m->velocity * delta_t;
    30. }
    31. // Reset all forces on each mass
    32. m->forces = Vector2D(0, 0);
    33. }
    34. }

     simulateVerlet

    1. void Rope::simulateVerlet(float delta_t, Vector2D gravity)
    2. {
    3. for (auto &s : springs)
    4. {
    5. // TODO (Part 3): Simulate one timestep of the rope using explicit Verlet (solving constraints)
    6. Vector2D ab = (s->m2->position) - (s->m1)->position;
    7. Vector2D f=-s->k*ab.unit()*(ab.norm()-s->rest_length);
    8. s->m1->forces-=f;
    9. s->m2->forces+=f;
    10. }
    11. for (auto &m : masses)
    12. {
    13. if (!m->pinned)
    14. {
    15. // TODO (Part 3.1): Set the new position of the rope mass
    16. m->forces += gravity*m->mass;
    17. Vector2D a = m->forces/m->mass;
    18. Vector2D lastPosition = m->position;
    19. //阻尼系数
    20. float damping_factor = 0.00005f;
    21. m->position += (1-damping_factor)*(m->position - m->last_position + a*delta_t*delta_t);
    22. m->last_position = lastPosition;
    23. // TODO (Part 4): Add global Verlet damping
    24. }
    25. m->forces = Vector2D(0,0);
    26. }
    27. }

  • 相关阅读:
    [2023.09.22]:探索Rust编写基于web_sys的WebAssembly编辑器:挑战输入光标定位的实践
    MacOS如何降级旧版本?macOS降级,从 Ventura 13.0至Monterey 12
    测试时间不够,你会如何处理?
    认识MT5平台:从功能到优势
    Python爬虫之xpath语法及案例使用
    一级造价工程师(安装)- 计量笔记 - 第六章第二节自动控制系统
    RHCE学习 --- 第五次作业
    ArcGIS加载免费在线历史影像作为底图(不需要插件)
    Arm Cortex R52与TC3xx Aurix上下文切换对比
    PHP:回退(Backed)枚举
  • 原文地址:https://blog.csdn.net/qq_48626761/article/details/126919638