Graphics/DirectX

Direct 3D - 사원수 (Quaternion)

MOLOKINI 2014. 7. 8. 10:04
사원수(quaternion)를 그래픽에서 사용하게 된 이유는 행렬로 회전을 나타낼 때와 비교하여 아래와 같은 차이가 있기 때문이다

                               행렬                                   사원수
데이터량               float 16개                               float 4개
연산속도               float 16 * float 16                     float 4 * float 4
결과의 질              Gimbal Lock등 오류발생           최단호 보간

행렬에 비해 연산속도도 빠르고 메모리 용량도 작으면 사원수를 쓰는게 당연하겠죠?
결정적으로 행렬은 오류도 발생할 수 있는데, 사원수는 행렬에 비해 좀 덜합니다.




사원수의 정의

사원수
사원수는 4차원 복소수 공간벡터로 다음과 같이 나타낸다

q = <x,y,z,w> = xi + yj + zk + w 혹은 q = s + v (단, s = w, v =< x, y, z>)

이때, i, j, k는 다음과 같은 성질이 있다.

i^2 = j^2 = k^2 = -1
ij = -ji = k
jk = -kj = i
ki = -ik = j

단위사원수
q = <0, 0, 0, 1> = 0i + 0j + 0k + 1을 단위 사원수라 한다.

켤레사원수(conjugate)
사원수 q = s + v의 켤레를 = s - v로 정의한다.
이 때, q = q = qq = ||q||^2 = q^2 이다.

역수                                                                  
                                                                                       
0이 아닌 사원수 q의 역수를 q^-1로 정의하면 q^-1 = q^2   이다.
분수알아보기가 힘드시죠잉? 켤레q/q^2 


사원수의 회전
사원수는 벡터의 회전과 밀접한 관계가 있는데, 축 A에 대해 각도 세타만큼의 회전을 사원수로 나타내면 다음과 같다.

          세타      세타
q = cos 2 + Asin 2

이렇게 만들어진 사원수로 점 P를 회전하려고 하면 qPq^-1 연산을 하면 된다. 이 연산의 결과로 나오는 값이 P를 축 A에 대해 각도 세타만큼 회전한 값이다.

최종적으로 얻어진 사원수를 실제 프로그램에서 회전에 사용하기 위해서는 다음과 같은 행렬로 다시 변환해야한다.

         [ 1 - 2y^2 - 2z^2            2xy - 2wz                 2xz + 2wy           0]
         [    2xy + 2wz            1 - 2x^2 - 2z^2              2yz - 2wx           0]
Rq =   [    2xz - 2wy                2yz + 2wx              1 - 2x^2 - 2y^2       0]
         [         0                             0                             0                  1]


사원수 보간

보간
보간은 중간을 채워넣는다는 뜻이야, 보간법에는 뉴턴법, 2진분할법 등 여러가지가 있는데, 선형보간이 제일 쉽다

선형보간
선형보간은 2개의 값을 점으로 생각하고 2개의 점을 이어주는 직선의 방정식으로 부터 값을 얻어내는 방법이다

2차원 선형보간

 

3차원 선형보간


직선 q(t) = (1-t)q1 + tq2는 t값이 0 -> 1로 변할 때 점 q1과 q2를 잇는 직선이 된다.


사원수 선형 보간 예

      t                               결과값

  t = 0.0                        q(t) = q1

  t = 0.3                        q(t) = 0.7q1 + 0.3q2

                                                                q1 + q2

  t = 0.5                        q(t) = 0.5q1 + 0.5q2 =     2

  t = 0.9                        q(t) = 0.1q1 + 0.9q2

  t = 1.0                        q(t) = q2

ㅇㅋ?



 선형보간의 문제점은 사원수 공간은 구면공간의 성격을 띄는데, 이를 선형보간하면 오차가 생긴다는 것,


이를 보정하기 위해서는, 정규화를 해주면 된다

정규화된 최종결과는 아래와 같다

            (1-t)q1 + tq2   

q(t) = ||(1-t)q1 + tq2||


하지만, 선형보간의 특성상 직선과 구면의 속도 오차가 생기게 되는데, 이를 해결하기 위해 구면 선형보간을 사용한다.



구면선형보간

4차원의 구면 위를 보간하는 구면선형보간(Slerp, Spherical Linear Interpolation)의 식을 유도하는 과정은 생략하고 결과만 보면 아래와 같다

         sin세타(1-t)            sin세타t

q(t) =     sin세타    * q1  +  sin세타  * q2      (단, 세타 = cos^-1(q1q2))


 구면 선형 보간의 기하학적 의미


사원수 공간은 특성상 구면의 성격을 띄게 되고, 사원수 값 q1과 q2는 구면 위의 한 점으로 표현될 수 있다. 구면 선형 보간이란 2개의 점 사이를 잇는 호의 중간 값을 구하는 것이라 할 수 있다.



사원수와 관련된 D3D 객체


  - 사원수 구조체

typedef struct D3DXQUATERNION {

        FLOAT x;

        FLOAT y;

        FLOAT z;

        FLOAT w;

} D3DXQUATERNION

단, q.x = sin(theta/2) * axis.x

     q.y = sin(theta/2) * axis.y

     q.z = sin(theta/2) * axis.z

     q.w = cos(theta/2)


  - 사원수 함수

함수명 : D3DXQUATERNION *D3DXQuaternionIdentity(D3DXQUATERNION *pOut);

용도 : 단위사원수를 구한다.

결과값 : (0,0,0,1)


함수명 : D3DXQUATERNION *D3DXQuaternionConjugate(D3DXQUATERNION *pOut, CONST D3DXQUATERNION *pQ);

용도 : 켤레사원수를 구한다.

결과값 : (x, y, z, w) -> (-x, -y, -z, w)


함수명 : D3DXQUATERNION *D3DXQuaternionInverse(D3DXQUATERNION *pOut, CONST D3DXQUATERNION *pQ);

용도 : 역수를 구한다.

결과값 : pQ의 역수가 pOut에 담겨 반환된다.


함수명 : D3DXQUATERNION *D3DXQuaternionMultiply(D3DXQUATERNION *pOut, CONST D3DXQUATERNION *pQ1, CONST D3DXQUATERNION *pQ2);

용도 : 2개의 사원수를 곱한다

결과값 : pOut = pQ1 * pQ2


함수명 : D3DXQUATERNION *D3DXQuaternionNormalize(D3DXQUATERNION *pOut, CONST D3DXQUATERNION *pQ);

용도 : 켤레사원수를 구한다.

결과값 : pOut = Q/|Q|


함수명 : D3DXQUATERNION *D3DXQuaternionSlerp(D3DXQUATERNION *pOut, CONST D3DXQUATERNION *pQ1, CONST D3DXQUATERNION *pQ2, FLOAT t);

용도 : 구면 선형 보간

결과값 : Q1과 Q2를 t값에 따라서 구면선형보간 한다.


함수명 : D3DXQUATERNION *D3DXQuaternionRotationMatrix(D3DXQUATERNION *pOut, CONST D3DXMATRIX *pM);

용도 : 회전행렬값으로부터 사원수를 만든다

결과값 : M행렬의 회전요소가 변환된 사원수


함수명 : D3DXQUATERNION *D3DXMatrixRotationQuaternion(D3DXQUATERNION *pOut, CONST D3DXQUATERNION *pQ);

용도 : 사원수로부터 회전행렬을 만든다

결과값 : Q 사원수로 부터 생성된 행렬 값


'Graphics > DirectX' 카테고리의 다른 글

Direct 3D - 텍스쳐  (0) 2014.07.21
Direct 3D - 조명모델  (0) 2014.07.12
Direct 3D - 렌더링 파이프라인  (1) 2014.06.19
Direct 3D - 행렬  (0) 2014.06.19
Direct 3D - 행렬기초  (0) 2014.06.19