我想在应用重力的同时在两点之间应用约束。我绘制的下图显示了点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;
}
您的代码中有三个问题。首先,为每个约束应用Euler积分,但它应该在每次迭代结束时仅应用一次。第二,点p1
应该是固定的。第三,你没有考虑力量计算中的质量。
要修复它,在force
结构中添加一个Point
向量并使用以下代码:
// 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
为系统添加阻尼,其中c
是damping ratio。
您没有正确计算加速度。力= m * a。乘以dt可以通过Euler积分获得速度。更好的集成方法有助于提高准确性。我想你只想要一个春天。摆一般意味着你想要一个固定的距离约束,但你认为你的意思只是一个重复的摆动位置。
警告:我没有把它通过编译器,所以我的加速可能是倒退。
另外我会考虑使用双缓冲位置。您不希望像这样计算循环中的所有位置,否则您可以根据不同时间步长中的位置计算力。
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;
}