【问题标题】:How to enforce a constraint between two points如何在两点之间强制执行约束
【发布时间】:2019-04-16 18:18:06
【问题描述】:

我想在两点之间应用约束,同时应用重力。我绘制的下图展示了点 2 的开始和结束位置,不包括中间时间步长位置,并假设点 1 具有固定位置:

我有一个点类定义如下:

class Point{
  glm::vec3 position;
  glm::vec3 op; // original position
  glm::vec3 velocity;
  float mass;
};

我可以定义两点并使用以下方法找到两点之间的原始长度:

Point p1;
p1.position = glm::vec3(0, 10, 0);
p1.op = p1.position;
p1.velocity = glm::vec3(0, 0, 0);
p1.mass = 1.0f;

Point p2;
p2.position = glm::vec3(10, 10, 0);
p2.op = p2.position;
p2.velocity = glm::vec3(0, 0, 0);
p2.mass = 1.0f;

float original_length_p1_p2 = glm::length(p2.op- p1.op);

我在点类内部有一个更新函数,它在某个时间步内运行,它应该通过应用重力来更新点位置:

glm::vec3 gravity(0,-9.8,0);
...
void update(float dt){
  velocity += gravity * dt;
  position += velocity * dt;
}

这些点存储在一个向量中,更新函数的调用如下:

std::vector<Point> myPoints;
...
for(int n = 0; n < myPoints.size(); n++){
  myPoints[n].update(dt);
}

现在我希望能够在这两个点之间应用一些类似弹簧的约束,它会像一个简单的类似弹簧的钟摆一样摆动。我尝试将以下内容添加到上述 for 循环中:

void applyConstraint(Point &p1, Point &p2, float dt){
    float change = (glm::length(p1.position-p2.position) - glm::length(p1.op-p2.op)) / glm::length(p1.position-p2.position);
    p1.position -= 0.5 * (p1.position-p2.position) * change * dt;
    p2.position += 0.5 * (p1.position-p2.position) * change * dt;
}

但是在尝试这个时,p2 没有任何约束。如何确保 p2 与图像相似?

更新 applyConstraint:

void Scene::applyConstraint(Point &p1, Point &p2, float dt) {
    float change = (glm::length(p1.position - p2.position) - glm::length(p1.op - p2.op)) / glm::length(p1.position - p2.position);
    glm::vec3 force = 0.5f * (p1.position - p2.position) * change * dt;
    glm::vec3 accel1 = (-force / p1.mass) * dt;
    glm::vec3 accel2 = (force / p2.mass) * dt;
    p1.velocity += accel1 * dt;
    p2.velocity += accel2 * dt;
    p1.position += p1.velocity * dt;
    p2.position += p2.velocity * dt;
}

【问题讨论】:

  • applyConstraint方法中,p1p2是通过拷贝传递的,所以点没有正确更新。
  • @Gilles-PhilippePaillé 我已更正以在我的代码中通过引用传递,但仍然得到相同的错误。还有其他想法吗?
  • 您正在计算弹簧力,但您将其用作速度。您应该修改速度,而不是修改位置。告诉我它是否有效。
  • @Gilles-PhilippePaillé 我尝试将力用作力,将其更改为加速度,然后更改为速度。我已经添加了更新的代码,但点仍然下降。

标签: c++ simulation game-physics glm-math


【解决方案1】:

您的代码中有三个问题。首先,您为每个约束应用欧拉积分,但它应该只在每次迭代结束时应用一次。其次,点p1 应该是固定的。第三,你在力计算中没有考虑质量。

要修复它,在Point 结构中添加一个force 向量并使用以下代码:

// Reset forces
p1.force = glm::vec3(0, 0, 0);
p2.force = glm::vec3(0, 0, 0);

// Add gravity
p1.force += gravity / p1.mass ;
p2.force += gravity / p2.mass ;

// Add spring forces
// To be put in applyConstraint, without dependency on dt
float k = 1 ;
glm::vec3 difference = p1.position - p2.position;
float current_length = glm::length(difference);
float original_length = glm::length(p2.op- p1.op);
float displacement = (current_length - original_length) / current_length;
p1.force -= k * displacement * difference ;
p2.force += k * displacement * difference ;

// Euler integration
p1.velocity += p1.force / p1.mass * dt ;
p2.velocity += p2.force / p2.mass * dt ;
//p1.position += p1.velocity * dt ; // This point is an anchor
p2.position += p2.velocity * dt ;

更改k以调整弹簧的弹性。如果您知道自己想要的行为,请使用this website 上给出的公式计算它。

您还可以使用p2.force -= c * p2.velocity 为系统添加阻尼,其中cdamping ratio

【讨论】:

  • 感谢您的更正和帮助。约束似乎非常有弹性,它要求字符串常数在 k = 100 左右才能看起来有点真实,为什么会这样?另外,阻尼常数是否会乘以p1.forcep2.force
  • 是的,这很正常。这完全取决于弹簧的单位和物理特性。增加阻尼也有助于稳定系统。我将编辑我的答案给你一些提示。
【解决方案2】:

您没有正确计算加速度。力 = m * a。乘以 dt 通过欧拉积分为您提供速度。更好的集成方法将有助于准确性。我想你只是想要一个春天。 Pendulum 通常意味着您想要一个固定的距离约束,但认为您的意思只是一个重复的摆动位置。

警告:我没有通过编译器将其放入编译器,因此我的加速可能会倒退。

另外我会考虑有一个双缓冲位置。您不想像这样计算循环中的所有位置,否则您可以根据不同时间步长中的位置计算力。

void Scene::applyConstraint(Point &p1, Point &p2, float dt) {

    //Our spring constant
    const float k = 0.5f;

    //Hooke's Law is F = -k*dX
    glm::vec3 dir = p1.position - p2.position;
    glm::vec3 force =  -k*dir;   

    glm::vec3 accel1 = (force / p1.mass);
    glm::vec3 accel2 = (-force / p2.mass);

    p1.velocity += accel1 * dt;
    p2.velocity += accel2 * dt;

    p1.position += p1.velocity * dt;
    p2.position += p2.velocity * dt;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-12-05
    • 2011-09-07
    • 2019-06-12
    • 2015-07-13
    • 1970-01-01
    • 2016-03-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多