KoreanFoodie's Study

2. DirectXMath 라이브러리의 행렬 다루기 본문

Game Dev/DirectX

2. DirectXMath 라이브러리의 행렬 다루기

GoldGiver 2021. 11. 15. 15:18

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

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


2. DirectXMath 라이브러리의 행렬 다루기

 

알아 두어야 할 개념들 : 

 

간단한 행렬 연산들

(행렬의 곱, 역행렬, 여인수, Transpose, Cramer's Rule 등)에 대한 설명을 여기에서는 다루지 않는다. 벡터와 행렬 연산에 대한 내용은 여기에서 참고하도록 하자. 대신 꼭 기억해야 할 것 한 가지만 짚고 넘어가려 한다.

 

가장 중요한 것 : 역행렬과 행렬식의 기하학적인 의미

두 벡터로 이루어진 행렬의 행렬식(determinant)은 평행사변형의 넓이이다. 이러한 평행사변형을 단위벡터 두 개로 이루어진 정사각형으로 변환한다고 가정해 보자(Identity Matrix 같은 녀석으로). 이때, 우리는 행렬식으로 변환한 행렬을 나누어주게 되는데, 이는 기하학적으로 평행사변형의 넓이를 1로 만들어준다는 뜻이다.

이러한 원리를 이해한다면, 추후 회전행렬의 행렬식이 왜 1인지를 직관적으로 이해할 수 있다. 왜냐하면 물체를 회전시킨다고 해서 물체의 크기가 바뀌지 않기 때문이다!

 

XMMATRIX

코드에서 벡터를 효율적으로 다루기 위해 DirectXMath 라이브러리의 XMMATRIX 형식을 사용한다. 이 형식은 SIMD 연산들을 이용해서 행렬을 효율적으로 처리한다. 클래스 자료 멤버에는 XMFLOATX4 형식을 사용하되, 필요에 따라 적재 함수 (XMLoadFloat4x4)를 이용해서 XMMATRIX로 변환하고 저장 함수(XMStoreFloat4x4)를 이용해서 다시 XMFLOAT4X4로 변환한다.

적재 함수는 FLOAT3, FLOAT4 등을 XMVECTOR나 XMMATRIX의 클래스로 바꿔주는 것이고, 저장 함수는 이러한 벡터와 행렬 클래스를 다시 FLOAT3, FLOAT4X4 따위의 형식으로 바꾸어 주는 것이다.

DirectXMath 라이브러리는 XMMATRIX를 이용한 행렬 덧셈, 뺄셈, 곱셈, 역행렬 등을 위해 중복적재된 연산자들과 편의용 함수들을 제공한다. 간단한 예제는 아래 코드를 참고하자.

#include <Windows.h> // for XMVerifyCPUSupport
#include <DirectXMath.h>
#include <DirectXPackedVector.h>
#include <iostream>

using namespace std;
using namespace DirectX;
using namespace DirectX::PackedVector;

// Overload the "<<" operators so that we can use cout to
// output XMVECTOR and XMMATRIX objects.
ostream& XM_CALLCONV operator << (ostream& os, FXMVECTOR v)
{
	XMFLOAT4 dest;
	XMStoreFloat4(&dest, v);

	os << "(" << dest.x << ", " << dest.y << ", " << dest.z << ", " << dest.w << ")";

	return os;
}

ostream& XM_CALLCONV operator << (ostream& os, FXMMATRIX m)
{
	for (int i = 0; i < 4; ++i)
	{
		os << XMVectorGetX(m.r[i]) << "\t";
		os << XMVectorGetY(m.r[i]) << "\t";
		os << XMVectorGetZ(m.r[i]) << "\t";
		os << XMVectorGetW(m.r[i]);
		os << endl;
	}

	return os;
}

int main() 
{
	// Check support for SSE2 (Pentinum4, AMD K8, and above)
	if (!XMVerifyCPUSupport())
	{
		cout << "directx math not supported" << endl;
		return 0;
	}

	XMMATRIX A(1.0f, 0.0f, 0.0f, 0.0f,
		0.0f, 2.0f, 0.0f, 0.0f,
		0.0f, 0.0f, 4.0f, 0.0f,
		1.0f, 2.0f, 3.0f, 1.0f);

	XMMATRIX B = XMMatrixIdentity();

	XMMATRIX C = A * B;

	XMMATRIX D = XMMatrixTranspose(A);

	XMVECTOR det = XMMatrixDeterminant(A);
	XMMATRIX E = XMMatrixInverse(&det, A);
	
	XMMATRIX F = A * E;

	cout << "A = " << endl << A << endl;
	cout << "B = " << endl << B << endl;
	cout << "C = A*B = " << endl << C << endl;
	cout << "D = transpose(A) = " << endl << D << endl;
	cout << "det = determinant(A) = " << det << endl << endl;
	cout << "E = inverse(A) = " << endl << E << endl;
	cout << "F = A*E = " << endl << F << endl;

	return 0;

}

 

전치행렬 (Transpose) 를 출력하는 함수

template <typename T, int m, int n>
void printTranspose(array<array<T, m>, n> arr)
{
	int m = arr.size();
	int n = arr[0].size();

	T** arrT = new T*[n];
	for (int i = 0; i < n; ++i)
		arrT[i] = new T[m];

	for (int i = 0; i < m; ++i)
		for (int j = 0; j < n; ++j)
			arrT[j][i] = arr[i][j];


	for (int i = 0; i < n; ++i)
	{
		for (int j = 0; j < m; ++j)
		{
			cout << arrT[i][j] << " ";
		}
		cout << endl;
	}

	for (int i = 0; i < n; ++i)
		delete[] arrT[i];

	delete[] arrT;


}

 

역행렬을 구하는 함수

void getCofactor(int mat[N][N], int temp[N][N], int p,
	int q, int n)
{
	int i = 0, j = 0;

	// Looping for each element of the matrix
	for (int row = 0; row < n; row++)
	{
		for (int col = 0; col < n; col++)
		{
			//  Copying into temporary matrix only those
			//  element which are not in given row and
			//  column
			if (row != p && col != q)
			{
				temp[i][j++] = mat[row][col];

				// Row is filled, so increase row index and
				// reset col index
				if (j == n - 1)
				{
					j = 0;
					i++;
				}
			}
		}
	}
}

/* Recursive function for finding determinant of matrix.
   n is current dimension of mat[][]. */
int determinantOfMatrix(int mat[N][N], int n)
{
	int D = 0; // Initialize result

	//  Base case : if matrix contains single element
	if (n == 1)
		return mat[0][0];

	int temp[N][N]; // To store cofactors

	int sign = 1; // To store sign multiplier

	// Iterate for each element of first row
	for (int f = 0; f < n; f++)
	{
		// Getting Cofactor of mat[0][f]
		getCofactor(mat, temp, 0, f, n);
		D += sign * mat[0][f]
			* determinantOfMatrix(temp, n - 1);

		// terms are to be added with alternate sign
		sign = -sign;
	}

	return D;
}


void printInverse(int arr[N][N])
{
	int det;


	det = determinantOfMatrix(arr, N);

	cout << "DeterminantOfMatrix : " << det << endl;

	for (int i = 0; i < 5; ++i)
		for (int j = 0; j < 5; ++j)
			arr[i][j] /= det;


	for (int i = 0; i < 5; ++i)
	{
		for (int j = 0; j < 5; ++j)
		{
			cout << arr[i][j] << " ";
		}
		cout << endl;
	}

}

 

Comments