2015년 5월 13일 수요일

[일기] 기분나쁜 일기장 HARD Constraints (7.1 ~ 7.2)

7. Hard Constraints ( 중 제약;? )

  지난 장에서 우리는 스프링들을 살펴보았고 만들었던 힘 발생기와, 용수철에 여러 물체를 연결시켰던 경우는 힘을 단지 다른 한 물체에만 적용시켰어야만 했음을 확인했다. 이번에는 다른 물체들의 움직임에 기반을 두어 행동하는 물체를 다룰 것이다.

  비록 용수철이 많은 상황을 표현하기 위해 사용될 수 있지만, 그들은 최악의 방식으로 행동하기도 한다. 우리가 만일 두 물체가 꽉 붙어서 이동하기를 원한다고 할때, 우리가 원하는 용수철 상수는 현실적으로 구현하기 불가능하다. 견고한, 딱딱한 막대에 의해서 연결되어있는 물체들을 시연하고자 하는 경우나 단단한 표면에 의해 거리를 유지하는 물체들을 시연하고자 할 때, 용수철은 실행 가능한 옵션이 아니다.

  이번 장에서는 우리는 강력한 제약에 대해 이야기할 것이다. 초기에 가장 흔하게 마주치는 제약을 살필 것이다 - 충돌과 물체간 접촉. 즉, 이전에 쓰인 수식들이 동일하게 다른 여러 종류의 제약들에 사용될 수 있는데, 막대나 늘어나지 않는 케이블이 그 예이고, 이들은 물체들을 함께 연결시키는 데 쓰일 수 있다.

  이러한 제약을 물리 엔진에서 극복하기 위해, 힘 발생기의 평온한 세계로부터 떠나야 한다. 모든 우리가 이 책에서 다루는 엔진은 힘 발생기와는 다른 방식으로 제약을 처리한다. 책의 막바지 부분에서는 가령 18장에서, 우리는 대안적인 접근을 브리핑 할 것이며 하나로 통합할 것이다.

 7.1 Simple Collision Resolution ( 단순 충돌 해결 )

  이러한 제약을 극복하기 위해, 우리는 충돌 해결 시스템을 우리의 엔진에 추가해야 한다.
  이 책의 진행을 위해서, '충돌'은 두 물체가 접촉하는, 겹치는 어떠한 상황이라도 부르는 용어로 정의한다. 영문 그 자체의 의미에서 우리는 충돌을 격렬한 절차로 이해하는 것이 보통이다. 이 경우 매우 큰 접근 속도(closing velocity;?)로 물체들은 닿는다. 우리의 의도상 이 또한 분명 사실이나, 두 물체가 단순히 접촉(touching)하는 것 즉, 거의 없는 접근 속도로(0이라도) 도달한다 할지라도 충돌로 생각하기로 하는 것이다. 비충돌접촉(resting contact)을 해결할 때도 빠른 속도로 충돌하는 물체들의 충돌을 해결할 때 쓰이는 절차와 같은 방식을 사용할 것이다. 따라서 위의 충돌의 의미 가정은 지당할 정도로 중요하다. 이 장의 마지막 부분에서 다양한 관점으로 깊숙이 살펴볼 것이다. 나중에 용어를 변경하는 것을 피하기 위해서, 이 장에서는 충돌(collision)과 접촉(contact) 두 용어를 서로 같은 것으로 간주하여 진행하기로 한다.

  두 물체가 충돌했을 때, 충돌 후의 물체들의 운동은 충돌 전의 물체들의 운동으로부터 계산할 수 있다. 이를 충돌 해결(collision resolution)이라 한다. 여기서 우리는 충돌 후 가능한 운동양상을 물체가 정확히 따르도록 하여 충돌을 해결한다. 충돌은 우리가 관찰할 수 없으리만큼 극히 짧은 시간동안에 발생하기 때문에 우리는 그 시간 안에서, 각 물체의 운동을 직접적으로 조작한다.

 7.1.1 The Closing Velocity ( 접근 속도 )

  충돌하는 강체들의 운동에 영향을 주는 법칙에는 그 강체들의 접근 속도가 반드시 고려된다. 'Closing Velocity'란 속도의 합으로써(상대 속도), 두 물체가 함께 움직일 때의 속도(서로의 상대적인)이다.

  이것은 속력 대신 속도임을 명심하라. 이 값이 아무리 스칼라량을 갖는다 하더라도 속도임을 잊지 말길 바란다. 속력(speed)은 방향이 없다. 이것은 단지 양의 값만을 갖는다. 속도는 방향을 가질 수 있다. 이때 어떤 스칼라 값이 있다면 방향은 값의 부호에 의해 결정된다. 그리고 서로 떨어져서(멀어지며) 이동하는 물체들은 0보다 작은 접근 속도를 갖게 된다(가까워지지 않는다).

  우리는 두 물체의 접근 속도를 각 한 물체의 속도 중 한 물체에서 다른 물체 방향으로의 속도 성분을 찾아냄으로써 계산해 낸다. 즉,

[접근속도식]


이고, Vc는 접근 속도를 뜻하며 스칼라 값을 갖는다. Pa와 Pb는 각각 물체 a와 b의 위치를 나타내고 점(·) 표기는 내적을 뜻한다. 그리고 p^은 p 벡터와 같은 방향으로의 단위 길이 벡터를 의미한다. 위 식은 다음과 같이 정리될 수 있다.

[식 7.1]


 비록 관습일 뿐이지만, 수식의 부호를 일부로 바꾸는 경우가 많다. 즉 이 의미는 접근 속도보다는 분리 속도(separating velocity)에 관심을 갖는다는 의미이다. 접근 속도는 한 물체가 다른 물체에 대한 상대적인 접근 속도를 의미하는 것이고 방향은 두 물체의 사이이다.

  두 물체가 서로에 대해 점점 다가가고 있는 경우에는 음의 상대 속도를 얻게 된다. 그리고 서로 멀어지는 물체에 대해서는 양의 속도를 얻게 된다. 수학적으로 이것은 단지 식 [7.1]의 부호 변환 문제일 뿐 별다를 바가 없다.

[식 7.2]


  여기서 Vs는 분리 속도를 의미하는데 이 책의 나머지에서 사용될 포맷이다. 물론 좋아한다면 계속 접근 속도를 고수해도 좋다. 비록 당신이 엔진에서 이를 벌충하기 위해 변수 수량의 부호를 반드시 뒤집어야 하겠지만, 이는 단지 선호의 문제일 뿐이다.

 7.1.2 The Coefficient Of Restitution ( 반발 계수 )

  지난 마지막 장에서 봤었듯이, 두 물체가 충돌할 때 그들은 서로 함께 압축된다. 그리고나서 표면의 용수철같은 변형은 두 물체를 분리시키기 위한 힘을 유발시킨다. 이 모든 것이 매우 짧은 시간의 간격 내에서 이루어진다(너무 짧아서 프레임마다 실행할 수 없다-물론 매우 빠른 속도의 영화필름에서는 충분한 길이일 것임에도 불구하고). 결국 두 물체는 더 이상 어떠한 접근 속도도 갖지 않게 된다. 비록 움직임이 용수철같았으나, 현실에서는 더욱 유사하다.

  모든 종류의 사건은 압축이 진행되는 동안 발생할 수 있으며 관련된 물질의 특성은 매우 복잡한 상호작용을 일으킬 수 있다. 그러나 현실에서는 나타나는 물체의 움직임은 감쇠 용수철의 행동과 동일하지 않고 컴퓨터로는 현실에서 나타나는 현상을 섬세하게 포착해 낼 수 없다.

  특히 용수철 모델은 충돌하는 동안 운동량이 보존된다는 가정을 하고 있다.

[식 7.3]


 여기서 Ma는 물체 a의 질량이며, pa는 물체 a의 충돌 전 속도이고 pa'는 충돌 후 속도이다.

  다행히도 충돌의 방대한 대다수는 이상적인 용수철과 같이 행동한다. 따라서 운동량 보존을 가정함으로써 신뢰할만한 물체의 행동방식을 완벽하게 만들어(알아낼)낼 수 있다. 여기서 식 [7.3]을 충돌처리 모델로 사용할 것이다.

 식 [7.3]을 통해 총체적으로 충돌 전후의 속도를 파악할 수는 있으나 각각의 개별 속도까지알아내는 것은 불가능하다. 각각의 속도들의 관계는 접근 속도라는 개념으로 서로 연결되어 있다. 수식에 따르면

[수식 Vs = -cvs]


 이고, 여기서 Vs'는 충돌 후의 분리속도를 뜻하고 Vs는 충돌 전의 분리 속도를 의미한다. 그리고 이 식에서 나타난 c가 바로 반발 계수이다.

  반발 계수를 통해 충돌 후에 물체들이 어떤 속력으로 멀어지게 될지를 결정할 수 있다. 충돌 때의 물질의 재질에 따라 다르다. 서로 맏닿는 재질의 쌍에 따라 반발 계수는 달라진다. 당구공이나 테니스 공과 같은 몇몇 물체들은 큐와 라켓에서 튕겨져 나간다. 눈덩이를 사람 얼굴에 던지면 즉, 이런 다른 이러한 종류의 물체들은 충동했을 때 서로 들러붙게 된다.

  반발계수가 1이면 충돌하는 물체들은 그들이 서로에게 접근해 오던 속도와 같은 크기의 속력을 갖고 서로 튕겨나가게 된다. 반발계수가 0이면 두 물체는 접착하고 함께 여행한다. 즉, 둘의 분리 속도는 0이다. 반발계수와 상관없이 식 [7.3]은 양변의 운동량이 같음을 보장한다.

  두 식을 사용하면 우리는 pa'와 pb'의 값을 알아낼 수 있다.

7.1.3 The Collision Direction And The Contact Normal ( 충돌방향과 충돌법선 )

  지금까지 두 물체 사이의 충돌에 관해 다뤘다. 하지만 종종 물리적인 시연을 하지 않는 것들과 한 물체간의 충돌도 지원해야 하는 경우도 있다. 이러한 물리적 시연의 대상이 아닌 것들은 지면(바닥)이거나 한 층의 장벽과 같은 움직이지 않는 물체들이 될 수 있다. 이러한 것들은 질량이 무한한 물체로 간주할 수 있으나 물체로 간주하고 충돌에 관한 속도를 계산하는 것은 어차피 움직이지 않을 물체에 대한 속도를 계산하는 것과 마찬가지이므로 시간 낭비다.

  만일 움직이지 않는 풍경적(?) 요소의 일부와 한 물체 사이의 충돌이 일어난다면, 각 물체의 위치를 기반으로 한 벡터를 고려하여 분리 속도를 개산할 수가 없다. 단지 한 물체 정보만을 알기 때문이다. 다시 말하면 우리는 식 [7.2]의 pa-pb 식을 계산할 수가 없다. 따라서 이 수식을 변경할 필요가 있다.

  이 pa-pb 표기는 분리 속도의 방향을 알려준다. 분리 속도는 두 물체와 저 표기의 내적을 통해 계산된다. 따라서 두 물체 정보를 갖지 않으면 그 방향을 명시적으로 지정해 줘야 한다. 이 방향이 두 물체가 충돌하고 있는 방향이며 대개 충돌 법선(collision normal or contact normal)이라 불린다. 이들은 방향을 나타내기 때문이고 이 백터의 크기는 항상 1이다.

  두 입자가 충돌하고 있는 경우에, 충돌 법선은 다음과 같이 주어질 것이다.

[법선벡터가 뭐의 빼기]


  관례상, 충돌 법선은 물체 A의 관점에서 계산한다. 이 경우 즉 a의 관점에서, 충돌은 b에서 a 방향이다. 따라서 pa-pb이다. b의 관점에서 충돌의 방향을 알고자 하는 경우 단순히 -1만 곱해주면 된다. 실제로는 우리는 이러한 일을 명시적으로 하지는 않지만 코드 내의 이러한 값의 반전은 b의 분리 속도를 계산하기 위해 사용되곤 했다. 아마 이 장의 마지막에 구현할 코드에서 이 요소가 있음을 알아차릴 것이다. : 마이너스 부호가 b의 계산에서 나타난다.

  입자가 땅과 충돌할 때는 a 물체만 있고 b 물체는 없는 것과 같다. 이 경우 a의 관점에서 충돌법선은 다음과 같이 된다.

[충돌법선식]


  이 때, 충돌점이 지면과 같은 높이에 있다고 가정한다.

  입자들이 완전한 강체들과 함께 운동하도록 두었을 때는 명시적인 충돌 법선을 갖는 것이 매우 중요해진다. 물체 내부 충돌을 위해서 특히 그렇다. 책의 나머지 부분을 예습하지 않은 경우 그림 [7.1]를 보고 어떤 상황을 우리가 고려하게 될지 대략적으로 알 수 있게 될 것이다.  그림에는 두 물체가 충돌하고 있으며 각 물체의 모양 덕분에 만일 그들의 위치만을 고려해서 계산했을 경우 얻게 될 충돌 법선과는 전혀 반대 방향을 갖는 충돌 법선을 갖는다. 즉 위치만을 고려해서 계산하면 풀릴 수 없다. 물체들의 아치형 부분이 겹쳤으며 따라서 충돌로 인해 두 물체는 서로 같이 붙어 있게 된다기 보단 서로 멀어지지 못하는 상황에 처하게 된다. 이 장의 마지막 부분에서 입자에 대한 이와 유사한 상황을 살펴볼 것이고 그 상황은 막대나 다른 단단한 연결을 표현하는데 나타날 것이다.

  올바른 법선 벡터를 구하는 식은 다음과 같다.

[식 7.4]

  [그림 7.1]

 7.1.4 Impulses ( 충격량 )

  우리가 충돌을 해결하기 위해 변경해야 하는 것은 물체들의 속도뿐이다. 지금까지 이 물리 엔진에서 가속도를 이용해 속도의 값들만을 바꿔왔다. 고로 가속도가 오랜 시간 적용되면 속도에는 큰 변화가 생기게 되었다. 여기선 허나 변화는 순간적이다. 속도는 즉각적으로 새 값을 갖게 된다.

  힘이 물체의 가속도를 바꾼다는 사실을 회상해보자. 만일 우리가 일시적으로 힘을 바꾼다면, 가속도도 순간적으로 바뀌게 된다. 또한 물체의 속도를 바꾸기 위해서도 같은 방식을 물체에 적용할 수 있다는 생각을 해볼 수 있다. 힘이라기보다는 충격량이라고 불리는 것을 통해서다. 충격량은 속도의 즉각적인 변화이다. 힘의 공식에서와 같은 방식으로

[힘 공식 에프엠에이]


우리는 다음을 생각할 수 있다.

[식 7.5]


 여기서 g는 충격량이다. 충격량은 종종 문자 p를 이용해 표기되지만 여기서는 물체의 위치를 나타내는 p와의 혼동을 피하기 위해 문자 g를 사용한다.

  유사한 형태의 두 식이지만 힘과 충격량 사이에는 큰 차이가 있다. 한 물체는 물체에 가해지는 힘이 없는 경우 가속도를 갖지 않는다. D'Alembert의 이론을 이용해서 힘의 합력을 통해 알짜힘에 대한 가속도를 구할 수 있음을 알 것이다. 이와는 반대로 한 물체는 물체에 어떠한 충격량이 작용하지 않는다고 하더라도 속도를 계속해서 갖는다. 따라서 충격량은 속도를 변화시키는 기능을 함을 알수 있다. 물론 충격량이 속도에 대해 완전한 책임이 있는 것은 아니다. 충격량 또한 D'Alembert의 이론을 사용하여 합할 수 있으나, 결과는 속도의 변화로 나타날 것이다. 전체 속도가 아님을 명심해야 한다.

[식 합 피와 시그마들]


  g1 ... g2 는 물체에 작용하는 충격량들의 집합이다. 실제로 우리는 힘에서 했던 것처럼 충격량을 누적하지는 않는다. 단지 충돌 해결 절차(?;collision resolution process) 동안에만 충돌량이 발생하므로 이들을 그 기간동안만 물체에 적용할 뿐이다. 각각은 한 순간에 다음과 같은 식으로 적용될 수 있다.

[피와 질량 쥐]


  충돌 해결의 결과는 각 물체에 적용해야할 충격량의 값이 된다. 충격량은 즉시 적용되며 당장에 물체의 속도를 변화시킨다.

7.2 Collision Processing ( 충돌 처리 )

  충돌을 다루기 위해 코드 몇 줄을 작성할 것이다. 클래스는 ContactResolver로 명명한다. 이 클래스는 충돌들의 집합을 보유하고 관련된 물체들에 관련된 충격량을 적용시키는 일을 맡을 것이다. 각 충돌은 Contact라는 데이터 구조를 통해 관리할 것이며 다음과 같이 코딩해 보도록 한다.

---- pcontacts.h 발췌 ----
---------------------------------------------------------------------
/**
* @details 충돌은 두 물체가 접촉하고 있음을 나타낸다. 충돌을 해결하는 것은 이들의 교차(겹침)를 없애는 것을 목적으로 한다. 따라서 충분한 충격량을 적용하여 그들을 떨어뜨려 놓는다. 충돌한 강체는 튀어오를 수 있다.
* 충돌에 호출가능한 함수는 없으며, 단지 충돌의 상세를 보유한다. 충돌의 집합을 해결하기 위해선 입자 충돌 해결사(?) 클래스를 사용한다.
*/
class ParticleContact {
   /// 충돌에 관련된 입자들에 대한 포인터이며 풍경요소와 충돌할 경우 두번째 포인터는 NULL 값을 가질 수 있다.
   Particle* particle[2];

   /// 충돌에 대한 반발 계수
   real restituion;

   /// 계 좌표계에서 충돌의 방향
   Vector3 contactNormal;
};
---------------------------------------------------------------------

구조체는 충돌에 관련되어 있는 각각의 물체에 대한 포인터와 충돌에 대한 반발계수를 보유한다. 그리고 충돌 법선의 경우 첫번째 물체 관점에서 기술된다. 만일 한 물체와 풍경 사이의 충돌을 처리하고 있다면(즉 물체가 하나만 관련되는 경우), 두 번째 물체를 가리키는 포인터의 값은 NULL이 된다.

  충돌을 해결하기 위해서 이 섹션의 앞부분에서 살핀 충돌에 관련된 수식을 적용할 것이다.

- pcontacts.h 발췌 -
---------------------------------------------------------------------
class ParticleContact {
    ///... 전에 기술한 부분

  protected :

    /// 충돌을 해결한다. 속도와 관통을 둘다 해결한다.
    void resolve(real duration);

    /** 분리 속도를 계산한다 */
    real calculateSeparatingVelocity() const;

 private:
    /** 충격량을 다룬다 */
   void resolveVelocity(real duration);
};
---------------------------------------------------------------------
- pcontacts.cpp 발췌 -
---------------------------------------------------------------------
#include <cyclone/pcontacts.h>
void ParticleContact::resolve(real duration) {
   resolveVelocity(duration);
}

real ParticleContact::calculateSeparatingVelocity() const {
   Vector3 relativeVelocity = particle[0]->getVelocity();
   if(particle[1]) relativeVelocity -= particle[1]->getVelocity();
   return relativeVelocity * contactNormal;
}

void ParticleContact::resolveVelocity(real duration) {
  /** 충돌 방향의 속도를 구한다 */
  real separatingVelocity = calculateSeparatingVelocity();

  /** 해결될 필요가 있는지 확인한다 */
  if( separatingVelocity > 0 ) {
      // 충돌이 분리되는 중이거나 고정되어 움직이지 않으면 충격량 적용 필요 없음
      return;
  }

  // 다시 새 분리 속도를 계산한다.
  real newSepVelocity = -separatingVelocity * restitution;

  real deltaVelocity = newSepVelocity - separatingVelocity;

  real totalInverseMass = particle[0]->getInverseMass();
  if( particle[1]) totalInverseMass += particle[1]->getInverseMass();

  //무한 질량이면 무시한다.
  if( totalInverseMass <= 0 ) return;

  real impulse = deltaVelocity / totalInverseMass;

  //역질량 단위의 충격량 크기 계산
  Vector3 impulsePerIMass = contactNormal * impulse;

  //충격량 적용
  particle[0]->setVelocity(particle[0]->getVelocity() + impulsePerIMass * particle[0]->getInverseMass());
  if(particle[1]) {
    particle[1]->setVelocity(particle[1]->getVelocity() + impulseIMass * -particle[1]->getInverseMass());
  }
}
---------------------------------------------------------------------

  이로 인해 두 물체의 속도가 직접적으로 바뀌고 충돌이 반영된다.

댓글 없음:

댓글 쓰기