Chapter 14. 충돌 처리 (2)
14.3.2 비선형 투영법 구현하기
비선형 투영법에 대해서 좀 더 자세하게 알아보고 이를 코드에 구현하는 법에 대해서도 알아보자.
우리는 이제 접촉 지점에서 발생하는 관통의 깊이에 대해서 알고 있다. 이는 곧 상호 관통을 처리하기 위해 필요한 운동량의 총합을
의미한다. 우리가 필요로 하는 것은 각 물체의 총 운동량 중에서 선운동량과 각운동량이 어느 정도의 비율을 차지하고 있느냐 하는 것이다.
일단 가정에서부터 시작해 보자. 우리가 시뮬레이션하려는 물체가 서로 부딪히면서 변형이 발생한다고 상상해 보자.
이 경우는 상호 관통의 총량보다는 변형의 총량으로서 접촉 지점에서 발생하는 관통의 깊이를 먼저 구할 필요가 있을 것이다.
속도를 계산하는 부분에서 이미 살펴보았듯이, 이러한 변형이야말로 각각의 물체가 서로를 밀어내는 힘의 원인이 된다.
달랑베르의 정리에 따르면 이 힘은 선 효과(linear effects)와 각 효과(angular effects)를 가진다.
이 각 효과의 합은 각 물체의 역질량과 역관성 텐서에 의해 결정된다.
상호 관통을 이런 방식으로 처리하는 것은 앞에서 살펴본 수학적인 과정을 똑같이 사용할 수 있다는 이점을 제공해준다.
우리는 선 성분과 각 성분을 찾기 위해 필요한 실제 물리학적인 근사치를 사용함으로써 두 물체가 어떻게 변형을 가하는 힘에 의해
서로 멀어지게 되는지를 효과적으로 모델링할 수 있을 것이다.
성분 계산하기
일단 우리가 필요로 하는 것은 하나의 임펄스 단위로부터 발생하는 회전 변화가 원인이 되는 속도 변화의 총량이다.
이는 충격이나 힘이 접촉 지점에 가해질 때 어떻게 저항을 가진 물체가 회전하게 되는지를 측정한 양이다.
관통의 문제를 해결하기 위해서는 동일한 방정식의 수열을 사용한다. 이를 통해 선운동 혹은 각운동하는 물체의 저항값을 구할 수 있다.
움직이고 있는 물체의 저항값을 관성(inertia)이라고 부른다는 것을 기억하라. 따라서 우리가 구하고자 하는 것은 각 물체의 접촉 법선상으로
어느 정도의 관성이 가해지느냐 하는 것이다. 이 관성은 선 성분과 각 성분을 가진다.
이전에도 살펴보았지만 관성의 선 성분은 단순하게 역질량을 의미한다. 각 성분은 우리가 앞서 사용했던 공식과 동일한 수열을 사용해
계산이 가능하다.
운동 적용하기
선운동을 적용하는 것은 간단하다. 선운동 값은 필요한 이동의 총량을 표현하며, 접촉 법선은 운동이 어느 방향으로 일어나야 하는가를
지시해준다.
1 | body[i]->position += contactNormal * linearMove[i]; | cs |
각운동의 경우는 조금 더 난해하다. 우리는 이미 선운동의 총량을 어떻게 구할지 알고 있다. 이를 위해서는 우선 쿼터니언의 변화를
계산할 필요가 있다. 3단계를 거쳐 이 총량을 구할 수 있다. 우선, 접촉 지점을 한 유닛 정도 움직이는 데 필요한 회전을 계산한다.
두 번째, 이 값을 필요한 유닛의 수만큼 곱한다. 예를 들어, angular-Move의 값만큼 곱할 수 있다. 마지막으로 회전을 쿼터니언에 반영하면 된다.
이전과 동일한 가정을 사용해 물체가 회전하는 데 필요한 방향을 계산할 수도 있다. 회전은 일종의 충격에 의해 발생한다.
비록 속도가 변화하지 않더라도 위치와 방향은 바뀐다. 접촉 지점에 충격이 가해지는 경우 회전의 변화는 다음과 같다.
q rel는 접촉 지점의 상대적인 위치를 의미하며, u는 충격에 의해 발생되는 충격 토크를, g는 이전과 같이 접촉 법선 방향으로의 충격을 의미한다.
코드는 다음과 같다.
1 2 3 4 5 | Vector3 inverseInertiaTensor; body->getInverseInertiaTensorWorld(&inverseInertiaTensor); Vector3 impulsiveTorque = relativeContactPosition % contactNormal; Vector3 impulsePerMove = inverseInertiaTensor.transform(impulsiveTorque); | cs |
이를 통해 한 유닛을 움직이는 데 필요한 충격 토크를 구할 수 있게 된다. 우리는 움직여야 하는 총 길이에 대해서 이미 알고 있고,
직접 물체를 움직일 수도 있으니 여기서 충격에 관심을 기울일 필요는 없다. 어떻게 힘이 운동으로 전환되는가에 대해서는 걱정할 필요가
없는 것이다. 한 유닛 정도의 움직임에 필요한 회전을 구하기 위해서는 여기에 관성값을 곱해주기만 하면 된다.
1 | Vector3 rotationPerMove = impulsePerMove * 1/angularInertia; | cs |
rotationPerMovevector를 통해 한 유닛만큼 이동하는 데 필요한 회전값을 알 수 있게 된다.
또한 우리가 알고자 하는 모든 운동의 총량이 angularMove로 표시된다는 것을 알 수 있으며, 따라서 회전의 총량은 다음과 같다.
1 | Vector3 rotation = rotationPerMove * angularMove; | cs |
회전을 반영하기 위해서는 식 9.9에서 사용했던 updateByVectorfmf 함수를 사용해야 한다.
14.3.3 과회전 방지하기
그림 14.8은 과하게 상호 관통이 발생한 물체를 보여주고 있다. 만약 관성 모멘트가 작지만 대신 물체의 질량이 클 경우 반영되는
요소 중에 가장 주요한 것은 각운동이 될 것이다. 아무리 많은 각운동이 가해진다고 하더라도 절대 접촉 지점은 물체에서 벗어날 수 없다.
예로 든 상황이 극단적일 수는 있지만 오히려 이런 문제는 더 현실적으로 보인다.
대규모의 회전이 발생하는 경우 우리는 또 다른 문제에 봉착한다. 물체를 너무 회전시켜 버리는 바람에 접촉 지점이 서로 다시 가까워지거나,
다른 물체의 일부분이 다시 관통될지도 모르는 가능성이 생기게 되는 것이다. 그림 14.9가 이를 보여주고 있다.
비록 그다지 큰 회전이 발생하지 않더라도 또 다른 관통을 유발할 수 있다.
이 두 문제 모두에서 회전의 총량을 제한할 필요가 있으며 이는 우리가 관통을 해결하는 방법의 일부가 될 것이다.
회전의 총량을 적게 유지한다는 것은 곧 회전이 적을 때를 고려한 우리의 가정이 여전히 유효하며, 특정한 하나의 관통 문제를 해결하는
과정에서 발생하는 또 다른 상호 관통의 경우를 최소화할 수 있다는 것을 의미한다.
우리가 원하는 선운동과 각운동의 총합은 4개의 변수로 계산되고 저장될 수 있다. 단일 물체의 충돌을 계산하는 경우라면 2개의 변수면 족할
것이다.
14.4 충돌 처리 프로세스
충돌 감지기는 수많은 충돌을 생성하고 이렇게 만들어진 모든 충돌이 적절한 방식으로 처리되어야 한다.
이를 위해 한 번에 모든 충돌을 처리할 수 있는 프레임워크를 만들 필요가 있다. 이 장의 마지막 부분에서는 이미 앞서 살펴본 알고리즘을
다시 활용할 것이다. 이 장에서는 마찰이 필요 없는 상황에서 사용 가능한 완전한 형태의 충돌 처리 시스템을 작성해 볼 것이다.
다음 두 개의 장에서는 마찰을 고려하고 처리 속도를 향상시킨 엔진에 대해서 다루어 볼 것이다. 또한 물체 간의 의존 관계를 어떻게
안정적으로 유지할 지에 대해서도 알아볼 것이다.
14.4.1 충돌 처리 파이프라인
그림 14.10은 충돌 처리 프로세스를 도식화해서 보여주고 있다. 충돌은 충돌 발생기에 의해 생성되며,
물체에 포함되어 있는 충돌 지오메트리에 기반해서 생성된다. 이렇게 생성된 일련의 충돌들은 충돌 데이터와 함께 충돌 처리 루틴을 따라
처리 된다.
충돌 처리 루틴은 크게 두 부분으로 나뉜다. 속도 처리 시스템과 관통 처리 시스템이 바로 그것이다. 이는 우리가 이 장에서 다루어 왔던 2개의
알고리즘과 맥을 같이 하는 것이다.
충돌 처리 루틴은 크게 두 부분으로 나뉜다. 속도 처리 시스템과 관통 처리 시스템이 바로 그것이다. 이는 우리가 이 장에서 다루어 왔던
2개의 알고리즘과 맥을 같이 하는 것이다.
이들 두 단계는 각각에 대해 독립적으로 수행된다. 물체에서 발생하는 속도의 변화는 상호 관통이 얼마나 깊게 발생하느냐는 문제에 영향을 미치지
않고, 그 반대의 경우도 마찬가지이다. 정밀한 프로세스를 거쳐서 속도를 처리하는 물리 엔진은 그와 함께 발생하는 모든 충돌을 동시에 처리하므로
우리가 앞서 구현했던 알고리즘을 사용하는 간단한 관통 처리 시스템을 별도로 가지고 있는 경우가 흔하다.
14.4.2 접촉 데이터 준비하기
각각의 접촉에 대해서 상호 관통 처리 단계와 속도 처리 단계를 모두 수행해야 하기 때문에 두 단계에서 공통적으로 사용되는 정보를 미리 계산해
놓는 것이 유용하다. 또한 계산에 필요한 처리 순서를 수정할 때 사용되는 추가적인 정보도 필요하다.
첫 번째 카테고리는 두 개의 정보로 구성된다.
- 접촉에 대한 기본적인 매트릭스는 calculateContactBasis 메서드에서 처리되며, contactToWorld를 호출한다.
- 각 물체의 접촉과 관계된 위치. 앞서의 코드에서 이 부분은 relativeContactPo-sition이라고 불렀다.
두 번째 카테고리는 접촉 지점에서의 상대 속도이다. 이는 속도 문제를 해결하기 위해 반드시 필요한 항목이므로 적절한 방법을 통해 이를 계산할
필요가 있다. 만약 우리가 발생하는 순서에 따라 충돌을 처리하려고 한다면 어떤 것을 먼저 처리해야 하는지 결정하기 위해 이 값이 필요하다.
따라서 한 번 계산된 이후 필요할 때마다 여러 번 재사용이 가능하다는 장점이 있다. 이 데이터들은 Contact 데이터 구조 안에 저장이 가능하다.
물체 스왑하기
상대 속도 계산하기
상대 속도는 접촉 지점의 두 물체에서 발생하는 접근 속도의 합을 말한다.
이를 통해 접촉 이후 튕겨나간 물체에게 요구되는 최종 속도를 구할 수 있다.
속도는 접촉 좌표상에서 구해진다. X값은 접촉 법선이 향하는 방향으로의 속도를 구할 수 있게 해주며, Y,Z값은 접촉 이후 얼마나 멀리
미끄러지는지를 결정한다. 이 두 값은 마찰을 고려하게 되는 이후의 장에서 충분히 활용될 것이다.
지금까지 살펴본 바와 같이 속도는 선 성분과 각 성분으로 구성된다.
우리가 구하고자 하는 q rel는 물체의 질량 중심에 따라 달라진다. p는 물체의 질량 중심을 의미하며, 'p는 두 물체의 선속도를 의미한다.
'θ는 물체의 각속도를 의미한다.
14.4.3 관통 처리하기
지금까지 2단계의 처리에 필요한 양쪽 물체의 접촉 지점에서 생성되는 데이터를 계산했다.
이제는 모든 접촉 지점에서 발생하는 상호 관통을 처리하는 데 집중해야 할 때이다.
이는 각각의 접촉을 순차적으로 처리하고 14.3절에서 살펴본 단일 접촉을 처리하는 알고리즘을 포함하는 메서드를 호출함으로써 수행이 완료된다.
이 모든 것들은 이전에 살펴본 prepareContacts와 동일한 방식을 사용함으로써 간단하게 구현이 가능하다.
1 2 3 4 5 | Contact* lastContact = contacts + numContacts; for(Contact* contact=contacts; contact < lastContact; contact++) { contact->applyPositionChange(); } | cs |
순차적으로 접촉을 살펴보면서 이와 함께 발생하는 상호 관통을 처리하는 것보다 충돌이 관통이 발생하는 순서대로 처리하는 것이 더 효율적이다.
각각의 단계에서 가장 깊은 관통값을 가진 충돌 지점을 찾아낸다. 이는 applyPositionChange 메서드를 통해 일상적으로 처리가 된다.
이 프로세스가 발생한 충돌의 개수만큼 반복되거나 더 이상 처리할 상호 관통이 남아 있지 않을 때까지 반복된다.
이 알고리즘은 동일한 접촉을 여러 번 다시 살펴볼 수 있다. 그림 14.12는 평평한 평면 위에 위치하고 있는 박스를 보여주고 있다.
첫 번째 모서리가 올라가면 두 번째 모서리는 더 깊게 평면을 파고들게 된다. 두 번째 모서리가 움직이면 첫 번째 모서리가 다시
관통하게 되는 식이다. 이런 방식이 충분히 반복되면 상황은 해결되고 어떤 모서리에서도 관통이 발생하지 않을 것이다.
보통은 반복의 한계에 다다라서야 이 문제가 해결된다. 만약 실제로 어느 정도의 반복이 발생하는지 체크한다면 이런 상황이
일반적이며 사용 가능한 모든 반복 과정을 거쳐야 한다는 것을 이해할 수 있을 것이다.
구현된 반복 알고리즘
관통 깊이 업데이트
14.4.4 속도 처리하기
이 알고리즘 역시 반복 수행을 기반으로 한다. 우선 각각의 이터레이션에서 가장 큰 접근 속도를 가진 충돌을 찾아낸다.
만약 접근 속도를 가진 충돌이 발견되지 않는다면 알고리즘은 종료된다. 조건에 해당하는 충돌을 찾아낸다면 이는 독립적으로 처리되며,
여기에서 이 장의 첫 부분에서 살펴본 메서드가 사용된다. 다른 접촉들은 앞선 변경에 기반해 업데이트된다.
만약 더 검증이 필요한 이터레이션이 있다면 알고리즘은 지속적으로 반복된다.
속도 업데이트
14.4.5 상대적인 업데이트 알고리즘
접촉 리스트는 접촉 데이터 구조상에서 두 개의 포인터로 연결되는 이중 연결 리스트(doubly linked list)의 형태로 작성된다.
이를 통해 특정 접촉의 이전 접촉과 이후 접촉을 식별 가능하게 된 것이다. 예를 들어, 충돌 처리 알고리즘을 보자.
동일한 설명이 속도 처리에서도 가능할 것이다. 일단 모든 접촉을 관통이 줄어드는 순서에 따라 이중 연결된 구조로 나열해 분류한다.
14.5 요약
충돌 처리 과정에는 지금까지 살펴본 것처럼 아주 복잡한 수학이 포함되어 있다. 하나의 접촉에 대해 일반적으로 두 단계의 처리 과정이 수행된다.
첫 번째는 물체 간의 상호 관통을 처리하는 과정이며, 두 번째는 그들의 접근 속도를 튕겨 나가는 속도로 전환해 주는 과정이다.
속도 처리 알고리즘에는 접촉 지점에 충격을 가함으로써 유발되는 영향을 고려하는 단계가 포함된다.
이를 통해 어느 정도의 충격이 있어야 바람직한 효과가 발생하는지를 계산할 수도 있다. 결과는 하나의 임펄스 값으로 도출되며 이를 통해
각 물체의 선속도와 각속도를 수정한다.
속도 처리 알고리즘과는 달리 관통 처리 과정은 이에 대응하는 물리적인 프로세스를 가지고 있지 않다.
현실에서는 강체가 서로 상호 관통하지 않기 때문이다. 이런 이유로 우리가 활용할 수 있는 방법에 제약이 있지만 여러 가지 다른 방법들을
고려해 시각적으로 충돌이라는 행위를 설명할 수 있어야 한다. 이 장에서는 속도를 처리 할 때 사용되었던 것과 동일한 압축과 충격에
관한 수학을 활용했다.
'Game > Physics & Math' 카테고리의 다른 글
Real Time Collision Detection - Chapter 5: Basic Primitive Tests (2) (0) | 2018.12.04 |
---|---|
Real Time Collision Detection - Chapter 5: Basic Primitive Tests (1) (0) | 2018.12.04 |
Game Physics Engine Development - Chapter 14 : 충돌 처리 (1) (0) | 2018.12.02 |
Game Physics Engine Development - Chapter 13 : 접촉 발생 (0) | 2018.12.02 |
Fast 3D Triangle-Box overlap Testing (0) | 2018.11.28 |