KoreanFoodie's Study

3. 선형변환, 아핀변환, 좌표 변환, DirectXMath 변환 함수 본문

Game Dev/DirectX

3. 선형변환, 아핀변환, 좌표 변환, DirectXMath 변환 함수

GoldGiver 2021. 11. 15. 21:06

DirectX 12 3D 게임 프로그래밍의 바이블

'DirectX 12를 이용한 3D 게임 프로그래밍 입문'을 읽으며 내용을 정리하고 중요한 부분을 기록하는 글입니다.


3. 선형변환, 아핀변환, 좌표 변환, DirectXMath 변환 함수

 

알아 두어야 할 개념들 : 

 

선형변환

선형변환은 벡터들 사이의 결합 연산으로 이해하면 된다. u + v는 벡터끼리의 합이고, k * v는 벡터 v에 스칼라 값 k를 곱한 scaling이다.

 

회전

벡터 v를 축 n에 대해 최전한다고 하자. 이러한 변환에서 회전각은  n을 내려다보는 방향(즉, n의 반대 방향)을 기준이로 시계방향으로 측정한다. 또한 ||n|| = 1 이라고 가정한다.

우선 v를 두 부분으로 분해한다. 하나는 n에 평행한 부분이고 또 하나는 n에 수직인(직교) 부분이다. 평행한 부분은 그냥 proj<n>(v)이다. 수직 부분은 perp<n>(v) = v - proj<n>(v)로 주어진다. (n이 단위벡터이므로 proj<n>(v) = (n * v) n 이다)

여기서 핵심은, n에 평행한 부분인 proj<n>(v)는 회전에 대해 불변(invariant)이므로 (즉, 벡터가 회전해도 변하지 않으므로), 수직인 부분을 회전하는 방법만 알아내면 된다는 것이다. 따라서 회전된 벡터 R<n>(v) 는 proj<n>(v) + R<n>(V_perp)만 구하면 된다.

R<n>(V_perp)를 구하기 위해, 회전 평면에 하나의 2차원 좌표계를 설정한다. v_perp를 두 기준 벡터(reference vector) 중 하나로 사용한다. 다른 하나는 v_perp와 n에 수직인 벡터이어야 한다. 그러한 벡터는 외적 n x v (왼손 엄지 법칙)로 구하면 된다.

2차원에서의 회전행렬 (원점 기준)
2차원에서의 회전행렬 (특정 좌표 기준)
3차원에서의 회전행렬은, 각 축에 대한 회전행렬의 곱으로 표현될 수 있다.

시각화를 해놓은 깔끔한 설명은 여기를 참고하자.

조금 더 수학적인 깊이를 원한다면 이 글을 읽어보자.

 

아핀변환

동차좌표

아핀변환(affine transformation; 또는 어파인변환, 상관변환)은 선형변환에 이동변환(translation transformation)을 결합(합성)한 것이다. 그런데 벡터는 위치와 무관하게 오직 방향과 크기만 서술하는 것이므로, 벡터에 대해서는 이동이 의미가 없다. 다른 말로, 벡터는 이동에 대해 불변이어야 하고, 이동은 오직 점(위치벡터)에만 적용해야 한다.

그런데 동차좌표(homogeneous coordiante)라는 것을 이용하면 점과 벡터를 동일한 방식으로 다룰 수 있다. 즉, 3차원 벡터에 w 성분을 추가해 주어진 동차좌표가 점인지 벡터인지 w값으로 표현한다. w 값이 0이면 벡터, w 값이 1이면 점이다.

 

아핀변환의 정의와 행렬 표현

3차원 그래픽에 필요한 변환 중에는 선형변환으로는 서술하지 못하는 것도 있다. 그래서 아핀 변환이라고 부르는 좀 더 넓은 부류의 함수들을 도입해야 한다. 아핀변환은 선형변환에 이동벡터 b를 더한 것이다. 이를 수식으로 표현하면 다음과 같다.

a(u) = t(u) + b

만약 b = 0이면 아핀변환이 그냥 보통의 선형변환과 같다. 따라서, 그 어떤 선형변환이라도 b = 0인 아핀변환으로 표현할 수 있다. 이는 임의의 선형변환을 4 x 4 아핀변환 행렬(줄여서 아핀 행렬)로 표기할 수 있다는 것이다.

 

변환들의 합성

S가 비례행렬이고 R이 회전행렬, T가 이동행렬이라고 하자. i = 0, 1, ..., 7인 여덟 정점 vi들로 이루어진 직육면체의 각 정점에 이 세 변환을 연달아 적용하려 한다. 행렬 곱셈은 결합법칙을 만족하므로, 이를 다음과 같이 표기해도 된다.

i = 0, 1, ..., 7에 대해 vi(SRT) = vi'''

행렬 C = SRT를, 세 가지 변환을 하나의 전체적인 변환으로 감싼 행렬로 간주할 수 있다. 즉, 행렬 곱셈은 여러 변환을 하나로 합치는 효과를 낸다. 좌표 변경도 결합법칙을 응용하여 하나의 행렬로 묶어 버릴 수 있다.

 

변환 행렬 대 좌표 변경 행렬

지금까지는 '능동적' 변환(비례, 회전, 이동)과 좌표 변경 변환을 구분했다. 그러나 사실 두 종류의 변환은 수학적으로 동치(equivalence) 관계이다.

물체 자체는 변경하지 않고 좌표계만 변환함으로써 물체의 좌표 표현이 바뀌게 하는 것이 더 직관적인 상황도 있고, 고정된 좌표계 내에서 물체 자체를 변환하는 것이 더 직관적인 상황도 있다.

 

DirectXMath 라이브러리의 변환 관련 함수들

DirectXMath 라이브러리의 변환 관련 함수들을 살펴보자.

// 비례행렬 생성
XMMATRIX XM_CALLCONV  noexcept XMMatrixScaling(
  [in] float ScaleX,
  [in] float ScaleY,
  [in] float ScaleZ // 비례 계수들
);

// 벡터의 성분들로 비례행렬 생성
XMMATRIX XM_CALLCONV  noexcept XMMatrixScalingFromVector(
  [in] FXMVECTOR Scale	// 비례 계수들
);

// x축에 대한 회전행렬 Rx 생성
XMMATRIX XM_CALLCONV  noexcept XMMatrixRotationX(
  [in] float Angle	// 시계방향 회전각 θ
);

// 이동행렬 생성
XMMATRIX XM_CALLCONV  noexcept XMMatrixTranslation(
  [in] float OffsetX,
  [in] float OffsetY,
  [in] float OffsetZ
);

// 벡터 대 행렬 곱  vM. 점 변환을 위해 v.w = 1로 둔다.
XMVECTOR XM_CALLCONV  noexcept XMVector3TransformCoord(
  [in] FXMVECTOR V,
  [in] FXMMATRIX M
);

// 벡터 대 행렬 곱  vM. 벡터 변환을 위해 v.w = 0로 둔다.
XMVECTOR XM_CALLCONV  noexcept XMVector3TransformNormal(
  [in] FXMVECTOR V,
  [in] FXMMATRIX M
);

 

 

 

Comments