[directx11]회전

Date:

카테고리:

회전행렬

회전행렬은 오일러 각도를 사용하여 3D 공간에서의 회전을 나타낸다.

회전행렬은 3x3으로 이루어져 있으며, 회전 변환을 직접 계산할 수 있고,

회전된 벡터를 계산하는 데 사용될 수 있다.


오일러각도 (Euler angles)

오일러각도는 우리가 흔히 표현하는 X, Y, Z 축을 각도로 표현한 방법중 하나이다.

이는 일반적으로 Yaw, Pitch, Roll로 표현된다.

물체의 방향을 표현하는 방법은 크게 고정각도와 오일러각도가 있다.

오일러각도는 d3d에서 로컬 좌표계에서 주로 쓰이며, 강체가 회전하면 축도 같이 회전한다.

따라서, 스켈레톤 등의 다양한 자세를 표현하기 위해 주로 쓰인다. 하지만 짐벌락 문제가 생길 수 있는 단점이 있다.


고정각도 (Euler fixed angles)

고정각도는 고정된 축 주위로 표현하는 방식이다. 각 축의 회전 각도가 고정되어 있고,

Z-X-Z, X-Y-X, Y-Z-Y와 같은 순서로 표현되는데, 순서에 따라 연속적으로 회전을 적용한다.

고정각도는 d3d에서 월드 좌표계에서 주로 쓰이며, 강체의 회전에 관계없이 고정된 축을 가지게 된다.

고정각도는 직관적으로 시퀀스를 정할 수 있기 때문에 일관된 결과를 기대할 수 있지만,

스켈레톤 등의 다양한 자세를 표현하기에는 매우 제한적이다.

Yaw, Pitch, Roll

Yaw, pitch, roll은 각각 오일러 각도로 표현되고, 다음과 같은 축에 대해 회전하는 각도를 의미한다(왼손좌표계 기준).

  1. Yaw (돌림각, Z 축 주위 회전) : 오브젝트를 Z축으로 회전시키는 각도이다.

  2. Pitch (경사각, Y 축 주위 회전) : 오브젝트를 Y축으로 회전시키는 각도이다.

  3. Roll (구르기 각도, X 축 주위 회전): 오브젝트를 X축 주위로 회전시키는 각도이다.


짐벌락

짐벌락은 오일러 각도에서 발생할 수 있는 문제인데, 세 개의 회전 축이 일직선에 가까워지는 상황에서 발생한다.

이렇게 되면 회전 축이 서로 중첩되는데, 회전이 정확하게 표현되지 않거나 예기치 않은 결과를 초래할 수 있다.

이러한 상황에서는 회전 자유도가 제한되며, 특정 방향으로의 회전이 다른 축의 회전에 영향을 받을 수 있다.

짐벌락은 오일러 각도, 특히 Yaw-Pitch-Roll 순서에서 발생할 가능성이 높다.

짐벌락을 회피하기 위해서는 오일러 각도 대신 쿼터니언이나 회전행렬과 같은 다른 회전 표현 방식을 사용해야 한다.

쿼터니언

쿼터니언은 3D 공간에서 회전을 표현하기 위한 수학적 개념이다.

쿼터니언은 스칼라와 벡터로 이루어진 4차원의 수를 뜻한다.

수치적으로 저장해놓기 좋지만, 계산해서 사용하기에는 매우 복잡하다.

수학적으로는 다음과 같은 형태로 표현된다.

\(q = w + xi + yj + zk\)
w는 스칼라 부분을 나타내고, (x, y, z)는 벡터 부분을 나타낸다.

벡터는 회전 축을 나타내고, 스칼라 w는 회전의 크기 또는 강도를 나타낸다.

  • 스칼라란 d3d에서 스케일 값을 나타낸다고 보면 된다.
  • 벡터는 크기와 방향을 가지는 3차원 값이다.
struct D3DXVECTOR3
{
    float x;
    float y;
    float z;
};

쿼터니언은 수학적인 연산과 속성을 갖고 있어서 회전을 수식으로 다루는데 유용하다.

  1. 곱셈 연산 : 두 개의 쿼터니언의 곱으로 회전을 합성할 수 있다.
    이는 회전을 순차적으로 적용하고, 회전의 조합과 보간에 유용하다.

  2. 덧셈 연산 : 두 개의 쿼터니언의 합으로 회전을 합성할 수 있다.
    이를 통해 회전을 결합하거나 보간하는 등의 작업을 수행할 수 있다.

  3. 역쿼터니언 연산 : 쿼터니언의 역원을 계산하여 반대 방향의 회전을 나타내는 작업이다.
    역쿼터니언은 회전을 취소하는 효과를 가지며, 회전의 되돌리기 작업에 사용될 수 있다.
    역쿼터니언을 구하는 방법은 이러하다.

    1. 주어진 수식
      \(q = w + xi + yj + zk\) 에 대해, 역쿼터니언, 즉
      \(q' = w' + x'i + y'j + z'k\) 을 찾는 것을 목표로 한다.

    2. 역쿼터니언은 다음과 같은 관계식을 만족한다
      \(q * q' = q' * q = 1\)
      주어진 쿼터니언 q와 역쿼터니언 q’에 대해, q와 q’를 곱한 결과는 항등원인 1이 되기 때문이다.

    이와 같이 벡터나 쿼터니언의 크기를 1로 만드는 과정을 정규화 라고 한다.

    따라서, 쿼터니언 q의 크기를 구한 후 정규화하여 단위 크기의 쿼터니언으로 만들어준다.

    그리고 쿼터니언의 스칼라 부분을 유지한 채로 벡터 부분의 부호를 반전시키는 작업을 한다.

    1. 쿼터니언 q의 크기를 구한다
      \(||q|| = sqrt(w^2 + x^2 + y^2 + z^2)\) 이는 피타고라스 정리의 일반화로 해석하면 된다.

    2. 쿼터니언 q를 정규화한다
      \(q_normalized = q / ||q||\)

    3. 역쿼터니언 q’의 스칼라 부분을 유지한 채 벡터 부분의 부호를 반전시킨다
      \(q' = q_normalized * (-1)\) 위의 과정을 거쳐 얻어낸 q’의 값이 바로 역쿼터니언이다.

  4. 회전 변환 연산 : 쿼터니언을 회전 행렬이나 오일러 각도로 변환하는 연산을 수행할 수 있다.
    회전 벡터를 쿼터니언으로 표현하고 쿼터니언 곱셈을 수행하면 회전값을 얻을 수 있다.

  5. 회전 보간 연산 : 두 개의 쿼터니언 사이를 보간하여 중간 회전을 구하는 연산을 수행할 수 있다.
    이를 통해 부드러운 회전 애니메이션을 생성하거나 회전 경로를 보간할 수 있다.
    이는 SLERP(Spherical Linear Interpolation)라고 하며, 구면 선형 보간을 사용한다.
    흔한 보간 중 하나는 코사인 보간인데, 수식은 이러하다.

\[Q_interp = (sin((1 - t) * θ) * Q1 + sin(t * θ) * Q2) / sin(θ)\]

여기서 t는 0과 1 사이의 값으로, 보간 정도를 나타내고, θ는 두 쿼터니언 사이의 회전 각도를 나타낸다.

하지만 이 보간법은 작은 각도일 때 사용되는 경우가 많고, 큰 회전 각도일 경우에는 정확한 결과를 보장하지는 않는다.

따라서, 이를 확장한 방법인 SQUAD(Spherical Cubic Interpolation) 등을 사용하기도 한다.

- SQUAD는 일반적으로 네 개의 쿼터니언을 사용하여 보간한다.   
보간하려는 쿼터니언 Q1, Q2의 중간 지점에 두 개의 보간 쿼터니언 S1과 S2를 정의한다.   
이를 통해 Q1에서 S1으로 보간하고, S1에서 S2로 회전한 다음, S2에서 Q2로 보간하는 과정을 거친다.   
이는 d3d에서 D3DX라이브러리에 구현되어 있다. 대표적으로 이러한 함수들이 사용된다.   
#include <d3dx.h>

D3DXQuaternionSquad:  개의 쿼터니언을 사용하여 SQUAD 보간을 수행

D3DXQuaternionSquadSetup: SQUAD 보간에 필요한 중간 보간 쿼터니언을 설정

D3DXQuaternionSlerp:  개의 쿼터니언 사이에서 SLERP(Spherical Linear Interpolation) 수행

D3DXQuaternionNormalize: 쿼터니언을 정규화

3DXQuaternionInverse: 역쿼터니언 계산

마치며

사실 대부분의 기능들은 구현되어 있기 때문에, 제대로 이해하고 함수를 사용하는 과정이 중요하다고 생각한다.

DirectX 카테고리 내 다른 글 보러가기

댓글 남기기