Как применить ограничение между двумя точками - PullRequest
0 голосов
/ 16 апреля 2019

Я хотел бы применить ограничение между двумя точками, применяя гравитацию.Следующее изображение, которое я нарисовал, демонстрирует начальную и конечную позиции точки 2, которая не включает промежуточные позиции временного шага, и предполагает, что точка 1 имеет фиксированную позицию:

Two points separated by a fixed constraint

У меня есть класс точек, определенный следующим образом:

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;
}

Ответы [ 2 ]

1 голос
/ 17 апреля 2019

В вашем коде есть три проблемы. Во-первых, вы применяете интеграцию Эйлера для каждого ограничения, но она должна применяться только один раз в конце каждой итерации. Во-вторых, точка 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, чтобы настроить упругость пружины. Если вы знаете, какое поведение вы хотите иметь, вычислите его по формуле, приведенной на на этом сайте .

Вы также можете добавить демпфирование в систему, используя p2.force -= c * p2.velocity, где c - коэффициент демпфирования .

0 голосов
/ 17 апреля 2019

Вы неправильно рассчитываете ускорение.Сила = м * а.Умножение на dt дает вам скорость через интеграцию Эйлера.Лучший метод интеграции поможет точности.Я думаю, ты просто хочешь весну.Маятник обычно означает, что вы хотите установить фиксированное расстояние, но думаете, что вы имеете в виду просто колеблющееся положение, которое повторяется.

Предупреждение: я не ставил это через компилятор, поэтому мои ускорения могут быть обратными.

Также я хотел бы рассмотреть двойную буферизованную позицию.Вы не хотите рассчитывать все позиции в цикле, как этот, иначе вы можете рассчитать силы на основе позиций на другом временном шаге.

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;
}
...