Gimbal Lock과 Quaternion

 

호랑이 담배피던 시절 아마 10년 가까이 된 듯한데,
아직 잡스횽의 iPhone 3, 3G가 국내에 상륙조차 하지 않고, 시중에 판매되는 거의 모든 폰이
감히 생명과 같은 Wifi를 못하도록 막아 놓고 패킷료 명목으로 돈 뜯어 먹던 어마어마한 억압의 시절 때였던 것 같다.

피쳐폰에 돌아가는 3D 게임을 만들려고 하니, 캐릭터가 게임에 나와야 했다.
캐릭터가 특정 모션을 부드럽게 해야했는데, 당시 피쳐폰 기반 엔진이 거의 전무하다시피 했는데
그나마 일본의 한 업체에서 개발한 Mascot Capsule Engine이 제일 쓸만한 때였었다.
허나 라이센스 비용이 어마어마했으니 섯불리 사용할 수도 없었고, 엔진의 Documentation도 허접 그자체였다.

결국 자체 에니메이션 엔진을 개발하기로 결정하게 되고 나와 다른 한명의 개발자가 개발에 착수했다.
Binary 파일 포멧을 정의한 후 Max Export를 제작하고 파일을 사용하기 좋게 로딩하는 Loader 파트를 내가 맡고,
다른 한분이 넘겨진 프레임별 bone 정보와 기본 skin 정보를 가지고 DFS traversing 하면서 최종 vertices들의 포지션을 계산해내는 로직을 맡아 작업을 했었더랬다.

2주 좀 넘어갈 즈음에 캐릭터가 OpengGL 화면상에서 제법 괜찮게 움직이는 것이 나왔다.
IKSelf-Shadow, Facial Animation 정도는 아직 꿈도 안꿨었다.
그냥 맥스에서 Bone 잡아서 프레임 에니메이션 넘긴 것이
OpenGL 화면에 제법 그럴싸하게 움직여 줘서 나와 coworker, 디자이너들 모두 신기해 했었다.
(이 엔진으로 나중에 모바일은 물론 온라인 게임도 만든다.)

기본 목표를 달성했으니 엔진 코드를 정리하고 게임 쪽 구현 부분을 넘어가려할 즈음에 조금 복잡한 에니메이션을
넣어서 테스트해 보려고 시도했었다. 팔을 휘둘리고 목을 꺽고 다리를 비트는…
어라, 근데 웬걸 동작이 꼬이면서 부자연스러웠다.
몇번이고 맥스쪽 데이터와 로더쪽을 살펴도 넘겨주는 값은 문제가 없었고,
DFS 로직을 사용하여 frame별 vertex들의 포지션을 구하는 로직도 별다른게 없었다.

결국 문제의 원인을 찾아낸 것이 바로 Gimbal lock으로 인한 회전 축의 소멸이었다.
Euler Angle을 사용하여 rotation matrix 로직을 구현하였던 우리는 Gimbal Lock까지 미쳐 생각지 못한 것.

gimbal

Gimbal Lock은 Euler Angle을 사용하여 Rotation을 구현할 때
특정 회전각에서 local 회전 축이 겹침으로 인해서 축이 소멸되는 현상이다.
X, Y, Z 순차 회전이 발생하는 Euler Angle Rotation 연산의 특성으로 인해 발생한다.
자연히 의도했던 회전이 발생하지 않고 이상한 결과값을 가리키게 된다.
Gimbal Lock을 잘 설명한 영상은 이게 아닐까 한다.

Gimbal lock을 피할 유일할 방법은 quaternion을 사용하는 것이라는 결론에 다다르고,
파일 포멧부터 익스포터 로더, 내부 로직까지 quaternion으로 전부 바꾸니 짜잔…
멋지게 돌아갔다.

Vector rotation과는 달리 Quaternion은 debug 시에 값만 보고 얼마나 회전했는지
i,j,k,w 값들을 보고 거의 알 수 없다. 또한 쿼터니언을 제대로 이해하기 위해서는
우리는 해밀턴에서부터 시작하여 양자와 우주를 논하는 스티븐 호킹이 되어야 한다. ㅎ
그냥 3차원의 회전을 완벽하게 하기 위해서, 허수부 3개와 실수부 1개로 구성되는
4차원의 쿼터니언 개념을 만들고, 여기서 회전 후에 3차원으로 이동시킨다 정도로만 알아둬도~ ㅎ
울프람 쿼터니언 참고: http://mathworld.wolfram.com/Quaternion.html

Unity 엔진에서는 Gimbal Lock을 해결하기 위해 내부적으로 Quaternion을 사용해 연산을 하며,
Quaternion Class를 지원해서 Euler Angle기반의 Vector와 Quaternion간 변환 및 Quaternion 연산과 관련다양한 static method를 지원하므로 바보가 되어 사용하면 된다. ㅎ