
다음과 같이 A의 열과 B의 행이 일치해야 곱이 정의된다.
행렬은 다음과 같이 분배 법칙과 결합 법칙이 성립한다.
and
DirectX는 row-major matrices를 사용하고 벡터 X 행렬 형태로 정의된다.
row-major matrices란 다음과 같은 행렬이 있을 때,
DirectX가 a -> b -> c -> d -> ... -> i 순으로 저장을 한다는 것이다. (행 중심)
DirectX의 수학연산(CPU)는 row-major를 사용하고 HLSL(쉐이더, GPU연산)은 column-major를 사용한다. (이는 Transpose를 이용함)
GPU가 column-major를 사용하는 것은 메모리 최적화를 위해서이다.
이를 무조건 따르는 것은 아니고 어떻게 설정했느냐에 따라 또 다르다.
정사각행렬 A의 Determinant는 다음과 같이 표기한다.
or
정의는 다음과 같다.
가 일 때, 는
이를 기하학적인 관점에서 volumes of boxes로 볼 수 있다.
각 벡터의 col 또는 row 벡터가 각각 이루는 면적이 인데 2차원이면 그냥 평행사변형이고 3차원 부터는 이를 쌓아 올리는 느낌이다.
정리하면 다음과 같다.
- n×n 행렬의 Determinant는 해당 행렬이 생성하는 기하학적 공간에서의 부피(volume)를 나타낸다.
- 2차원에서는 평행사변형의 면적, 3차원에서는 평행육면체(parallelepiped)의 부피를 의미함.
A가 정사각행렬일때 Cofactor of :
Cofactor matrix of :
Adjoint of :
Inverse matrix of :
이 또한 XMVECTOR에서와 마찬가지로 SIMD냐 ARM SIMD냐에 따라 달라진다.
#ifdef _XM_NO_INTRINSICS_
struct XMMATRIX
#else
XM_ALIGNED_STRUCT(16) XMMATRIX
#endif
{
#ifdef _XM_NO_INTRINSICS_
union
{
XMVECTOR r[4];
struct
{
float _11, _12, _13, _14;
float _21, _22, _23, _24;
float _31, _32, _33, _34;
float _41, _42, _43, _44;
};
float m[4][4];
};
#else
XMVECTOR r[4];
#endif
XMMATRIX() = default;
/* ... */
};
코드를 보면 union이라는 키워드를 볼 수 있다. 이는 C++에서 여러 개의 멤버 변수가 같은 메모리 공간을 공유하도록 하는 기능을 제공한다.
union
{
XMVECTOR r[4];
struct
{
float _11, _12, _13, _14;
float _21, _22, _23, _24;
float _31, _32, _33, _34;
float _41, _42, _43, _44;
};
float m[4][4];
};
이 union을 분석하면 XMMATRIX가 3가지 방식으로 저장됨을 알 수 있다.
- r[4]:
XMVECTOR타입의 배열로 저장- struct: 4x4 행렬을 개별 float 변수로 표현
- m[4][4]: 4x4 배열 형태로 표현
모든 멤버가 같은 메모리 공간을 공유해서, 다음과 같이 _11 = 1.0f;라 하면, m[0][0]도 1.0f가 된다.
이를 통해 메모리를 절약할 수 있고 같은 데이터를 XMVECTOR나 float 배열 혹은 개별 요소처럼 유연하게 표현할 수 있다.
MFLOAT4x4
XMMATRIX는 4개의 XMVECTOR instance를 사용한다. (A는 4*4 floating point matrix)
struct XMFLOAT4X4
{
union
{
struct
{
float _11, _12, _13, _14;
float _21, _22, _23, _24;
float _31, _32, _33, _34;
float _41, _42, _43, _44;
};
float m[4][4];
};
XMFLOAT4X4() = default;
XMFLOAT4X4(const XMFLOAT4X4&) = default;
XMFLOAT4X4& operator=(const XMFLOAT4X4&) = default;
XMFLOAT4X4(XMFLOAT4X4&&) = default;
XMFLOAT4X4& operator=(XMFLOAT4X4&&) = default;
constexpr XMFLOAT4X4(float m00, float m01, float m02, float m03,
float m10, float m11, float m12, float m13,
float m20, float m21, float m22, float m23,
float m30, float m31, float m32, float m33) noexcept
: _11(m00), _12(m01), _13(m02), _14(m03),
_21(m10), _22(m11), _23(m12), _24(m13),
_31(m20), _32(m21), _33(m22), _34(m23),
_41(m30), _42(m31), _43(m32), _44(m33) {}
explicit XMFLOAT4X4(_In_reads_(16) const float* pArray) noexcept;
float operator() (size_t Row, size_t Column) const noexcept { return m[Row][Column]; }
float& operator() (size_t Row, size_t Column) noexcept { return m[Row][Column]; }
};
이 또한 XMVECTOR와 XMFLOAT3간 변환 함수가 있었던 것처럼 XMMATRIX와 XMFLOAT4x4 변환 함수가 존재한다.
XMMATIX XM_CALLCONV XMLoadFloat4X4 ( const XMFLOAT4X4 *pSource) noexcept; // XMFLOAT4X4를 XMMATRIX로 load
void XM_CALLCONV XMStoreFloat4X4 ( // XMMATRIX를 XMFLOAT4X4에 저장
[out] XMFLOAT4X4 *pDestination, // output
[in] (FXMMATRIX M) // input
) noexcept;
다음 예제의 실행결과를 보면 실제 계산한 값과 동일함을 알 수 있다.
// 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"; // 여기의 operator<< 함수는 위에 있는 함수임
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 (Pentium4, 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;
}
실행결과는 다음과 같다.
A =
1 0 0 0
0 2 0 0
0 0 4 0
1 2 3 1
B =
1 0 0 0
0 1 0 0
0 0 1 0
0 0 0 1
C = A*B =
1 0 0 0
0 2 0 0
0 0 4 0
1 2 3 1
D = transpose(A) =
1 0 0 1
0 2 0 2
0 0 4 3
0 0 0 1
det = determinant(A) = (8, 8, 8, 8)
E = inverse(A) =
1 0 0 0
0 0.5 0 0
0 0 0.25 0
-1 -1 -0.75 1
F = A*E =
1 0 0 0
0 1 0 0
0 0 1 0
0 0 0 1