이전 단계인 Narrow Phase를 통해 우리는 충돌마다 Manifold들을 생성할 수 있었다.
struct Manifold
{
float seperation; // 충돌 깊이
glm::vec3 normal; // 충돌 방향
glm::vec3 pointA; // 오브젝트 A의 충돌 지점
glm::vec3 pointB; // 오브젝트 B의 충돌 지점
};
Solve 단계에서는 위 정보를 토대로 VelocityConstraints와 PositionConstraints를 처리한다.
VelocityConstraints는 두 물체가 충돌한 경우 발생한 충격량에 따라 속도를 변화시켜주는 것을 말한다. 이 Solve VelocityConstraint 단계에서는 충돌 정보인 Manifold뿐 아니라, 두 물체의 질량, 관성 텐서, 상대 속도 등 물리적 속성들에 의해서도 결과 값이 달라진다.
PositionConstraints는 충돌 시 두 물체의 겹침을 해소시켜주는 것을 말한다. 실제 세계의 물리와는 다르게, 물리 엔진에서는 프레임 별로 물체를 이동하다 보면 두 물체가 겹침이 발생하고 이를 충돌했다고 본다. 따라서 Narrow Phase에서 생성한 Manifold의 정보와 물체의 물리적 속성을 이용하여 두 물체를 분리시켜 겹침을 해소시켜 자연스러운 처리를 해야 한다.
void Island::solve(float duration)
{
int32_t bodyLength = m_bodies.size();
m_positions.resize(bodyLength);
m_velocities.resize(bodyLength);
for (int32_t i = 0; i < bodyLength; i++)
{
Rigidbody *body = m_bodies[i];
m_positions[i].position = body->getPosition();
m_positions[i].orientation = body->getOrientation();
m_velocities[i].linearVelocity = body->getLinearVelocity();
m_velocities[i].angularVelocity = body->getAngularVelocity();
}
ContactSolver contactSolver(duration, m_contacts, m_positions, m_velocities);
for (int32_t i = 0; i < VELOCITY_ITERATION; ++i)
{
contactSolver.solveVelocityConstraints(VELOCITY_ITERATION);
}
for (int32_t i = 0; i < POSITION_ITERATION; ++i)
{
bool isConstraintSolved = contactSolver.solvePositionConstraints(POSITION_ITERATION);
if (isConstraintSolved)
{
break;
}
}
for (int32_t i = 0; i < bodyLength; ++i)
{
Rigidbody *body = m_bodies[i];
body->updateSweep();
body->setPosition(m_positions[i].position);
body->setOrientation(m_positions[i].orientation);
body->setLinearVelocity(m_velocities[i].linearVelocity);
body->setAngularVelocity(m_velocities[i].angularVelocity);
body->synchronizeFixtures();
}
}
위 코드는 Island 별로 Solve를 진행하는 코드로 정리하면 다음과 같다:
만약 Island의 물체가 A, B, C 3개이고 이를 반복 없이 한 번에 처리한다고 가정해보자:
위와 같이 충돌이 발생한 경우 먼저 충돌 1을 처리하면 A와 B의 속도, 각속도 혹은 위치, 각도가 변경될 것이다. 그 후 충돌 2를 처리하려고 보면, B는 이미 충돌 1의 처리에 의해 충돌 당시에 비해 값이 너무 크게 바뀌어 버린 것을 볼 수 있고 충돌 2의 처리는 처음 충돌이 발생했을 때와는 다른 유형의 충돌이 되어버린다. 즉 그대로 충돌 2를 처리하면 부자연스러운 결과가 나오게 된다.
실제로 처음 구현 당시 충돌 처리를 한 번에 처리를 하였더니 부자연스러운 결과가 나왔었고, 그때 왜 Box2D에서 충돌 처리를 Iteration 만큼 반복했는지 알 수 있었다. 그래서 현재는 Island의 모든 충돌을 반복적으로 순회처리함으로써 자연스러운 충돌 처리를 구현할 수 있게 되었다.
실제 Box2D에서는 다음과 같은 추가 과정을 통해 더욱 자연스러운 결과를 만든다
현재는 기본적인 기능만을 구현하기 위해 이러한 부분들을 제외했지만, 최적화가 완료된 후 기능 업데이트를 위한 시간이 생긴다면 추가로 고려해볼 만한 부분인 것 같다.
FT_Newton: Cylinder 와 Capsule 충돌 - (10) (0) | 2025.01.16 |
---|---|
FT_Newton: Sphere 와 Box 충돌 - (9) (0) | 2025.01.16 |
FT_Newton: Narrow Phase / Clipping - (7) (0) | 2025.01.15 |
FT_Newton: Narrow Phase / EPA - (6) (0) | 2025.01.15 |
FT_Newton: Narrow Phase / SAT vs GJK - (5) (0) | 2025.01.15 |