LabHub
배우기 러닝패스 코스

물리 엔진의 뼈대 · 충돌 반응 · 이론

충격량 한 방으로 끝낸다

LabHub 에서 이어서 보기

한 줄 요약

충돌 반응은 힘이 아니라 충격량으로 계산합니다. 두 물체가 서로 멀어지도록 속도를 한 번에 바꿔 주는 값을 풀어서 곧바로 적용합니다.

왜 이게 필요했나

충돌을 힘으로 다루려면 접촉하는 동안의 힘을 시간에 대해 적분해야 합니다. 그런데 단단한 물체의 접촉 시간은 1밀리초도 되지 않아서, 60분의 1초 간격으로 도는 시뮬레이션에서는 그 순간이 통째로 한 걸음 안에 들어갑니다. 힘을 아무리 크게 잡아도 한 걸음 안에서는 물체가 이미 서로를 파고든 뒤입니다.

그래서 물리 엔진은 접촉 과정을 흉내 내지 않고 결과만 계산합니다. 충돌 전후의 속도 관계를 방정식으로 세우고 그것을 만족하는 속도 변화량을 한 번에 구합니다. 이 변화량이 충격량이고, 단위는 힘 곱하기 시간, 즉 운동량과 같습니다.

어떻게 동작하나

두 물체의 상대 속도를 충돌 법선 방향으로 본 값이 핵심입니다.

v_rel = dot(v2 - v1, n)        # n 은 1에서 2를 향하는 단위 벡터

이 값이 양수면 두 물체는 이미 멀어지는 중이라 아무것도 하지 않습니다. 이 검사를 빠뜨리면 이미 떨어지는 물체를 다시 잡아당기게 되어 물체가 표면에 달라붙습니다.

음수면 충격량을 구합니다. 반발 계수 e 는 충돌 뒤 상대 속도가 충돌 전의 몇 배로 되돌아오는지를 정하는 값으로, 1 이면 완전 탄성(에너지 보존), 0 이면 완전 비탄성(붙어서 같이 움직임)입니다.

j = -(1 + e) * v_rel / (inv_m1 + inv_m2)v1 -= j * inv_m1 * nv2 += j * inv_m2 * n

여기서 질량이 아니라 역질량을 쓰는 것이 관례입니다. 나눗셈을 곱셈으로 바꿔 조금 빠른 것도 있지만, 진짜 이유는 움직이지 않는 물체를 역질량 0 으로 자연스럽게 표현할 수 있기 때문입니다. 무한대 질량을 코드에 넣으면 나눗셈마다 특수 처리가 필요한데, 역질량 0 은 그냥 숫자입니다.

이 계산은 운동량을 항상 보존합니다. 두 물체가 크기가 같고 방향이 반대인 충격량을 받기 때문에 구조적으로 그렇습니다. 반면 운동에너지는 e 가 1 일 때만 보존되고, 그보다 작으면 그만큼 사라집니다. 사라진 에너지는 현실에서 소리와 열과 변형으로 갑니다.

마찰은 같은 계산을 접선 방향으로 한 번 더 하되, 크기에 상한을 둡니다.

t = normalize(v - dot(v, n) * n)      # 법선 성분을 뺀 나머지 = 접선 방향jt = -dot(v, t) / inv_m               # 접선 속도를 0 으로 만드는 값jt = clamp(jt, -mu * jn, +mu * jn)    # 쿨롱 마찰의 원뿔

접선 속도를 완전히 없애려면 얼마가 필요한지 먼저 구하고, 그 값이 mu * jn 을 넘으면 잘라 냅니다. 넘지 않으면 물체가 그 자리에 멈추고(정지 마찰), 넘으면 잘린 값만큼만 적용되어 미끄러집니다(운동 마찰). 조건문 하나로 두 마찰이 다 나옵니다.

회전이 들어가면 식이 조금 길어집니다. 접촉점이 무게중심에서 떨어져 있으면 충격량이 회전도 만들어 내기 때문입니다. 그때는 분모에 각 물체의 관성 모멘트에서 오는 항이 더해지고, 충격량을 적용할 때 선속도뿐 아니라 각속도도 함께 바꿔야 합니다. 구조는 같습니다 — 상대 속도를 구하고, 그것을 원하는 값으로 만드는 충격량을 풀고, 적용합니다. 이 실습이 회전을 빼는 이유는 식의 뼈대를 먼저 보게 하려는 것입니다.

반발 계수는 물체의 성질이 아닙니다. 두 물체가 만나는 조합의 성질이고, 부딪히는 속도에 따라서도 달라집니다. 실무에서는 두 물체의 값을 곱하거나 작은 쪽을 택하는 식으로 대충 합치는데, 어느 쪽이든 물리적인 근거는 약합니다. 값을 정확히 맞추려 애쓰기보다 원하는 느낌이 나오는 값을 고르는 편이 낫습니다.

현장에서 만나는 모습

낮은 높이에서 계속 튀는 물체가 멈추지 않고 미세하게 떨리는 문제가 흔합니다. 반발 계수가 작아도 0 이 아니면 튀는 속도가 0 으로 수렴하지 않기 때문입니다. 실무의 해법은 상대 속도가 어떤 문턱보다 작으면 반발 계수를 0 으로 강제하는 것입니다.

또 하나는 여러 접촉이 동시에 있을 때입니다. 접촉을 하나씩 순서대로 풀면 나중에 푼 것이 앞서 푼 것을 망가뜨립니다. 그래서 같은 걸음 안에서 여러 번 반복해 푸는데, 이것이 다음 모듈의 주제입니다.

마지막으로 부호와 방향의 약속을 한 번 더 짚어 두겠습니다. 법선이 1에서 2를 향한다고 정했으면 코드 전체가 그 약속을 지켜야 합니다. 감지 쪽에서 반대로 돌려주면 물체가 서로를 밀어내는 대신 끌어당기고, 그 증상은 "물체가 붙어서 떨어지지 않는다" 로 나타나 원인을 짐작하기 어렵습니다. 그래서 감지 함수와 반응 함수 사이의 이 약속은 주석이 아니라 시험으로 못 박아 두는 편이 안전합니다.

다음 실습에서 할 것

충격량 공식을 직접 만들어 반발 계수에 따라 운동량과 에너지가 어떻게 되는지 숫자로 확인하고, 공이 튀는 높이가 매번 배로 줄어드는 것을 그래프로 봅니다. 그다음 쿨롱 마찰로 미끄러지는 상자가 멈추는 거리를 구해 해석적인 답과 맞춰 보고, 마지막에는 공 다섯 개를 상자 안에서 튀게 해 궤적을 그림으로 남깁니다.