-
[DirectX] 3-4. Matrix4 클래스 제작Game Development/DirectX 2024. 6. 21. 12:12
이번 글에서는 행렬 클래스인 Matrix4 클래스를 구현해 보도록 하겠습니다.
Matrix4 클래스를 제작하기 전에 먼저 수학과 관련된 것들을 정의 해 놓을 Math 클래스를 만들도록 하겠습니다.
// Math.h #pragma once class Math { public: // 파이값 static constexpr float PI = 3.14159265f; // 호도법으로 static constexpr float Deg2Rad = (PI / 180.0f); // 60분법으로 static constexpr float Rad2Deg = (180.0f / PI); // 실수 두 개의 내용 교환 static void Swap(float& left, float& right); };
// Math.cpp #include "Math.h" void Math::Swap(float& left, float& right) { float t = left; left = right; right = t; }
이렇게 해서 Math 클래스를 만들었고,
이어서 Matrix4 클래스를 만들도록 하겠습니다.
// Matrix4.h #pragma once #include "Vector3.h" #include "Math.h" class Matrix4 { public: Matrix4(); // 단위 행렬 static Matrix4 Identity(); // 이동 행렬 static Matrix4 Translation(Vector3 pos); static Matrix4 Translation(float x, float y, float z); // 이동 행렬 역행렬 static Matrix4 InverseTranslation(Vector3 pos); static Matrix4 InverseTranslation(float x, float y, float z); // 회전 행렬 static Matrix4 Rotation(Vector3 rot); static Matrix4 Rotation(float x, float y, float z); // 회전 행렬 역행렬 static Matrix4 InverseRotation(Vector3 rot); static Matrix4 InverseRotation(float x, float y, float z); // 각 축 회전 행렬 static Matrix4 RotationX(float angle); static Matrix4 RotationY(float angle); static Matrix4 RotationZ(float angle); // 크기 행렬 static Matrix4 Scale(Vector3 scale); static Matrix4 Scale(float x, float y, float z); // View 행렬 static Matrix4 ViewLookAt(Vector3 pos, Vector3 target, Vector3 up); static Matrix4 ViewEuler(Vector3 pos, float pitch, float yaw); // Projection 행렬 static Matrix4 ProjectionPerspective(float fov, float width, float height, float near, float far); static Matrix4 ProjectionOrthographic(float width, float height, float near, float far); // 전치 행렬 static Matrix4 Transpose(const Matrix4& target); // 행렬의 요소 반환 float& Get(int row, int col); // 행렬 곱셈 void Mul(Matrix4& ret, Matrix4 oth, int row, int col); // 행렬 연산 Matrix4& operator=(const Matrix4 oth); Matrix4 operator*(const Matrix4 oth); Matrix4 operator*=(const Matrix4 oth); // 행렬 * 벡터 Vector3 operator*(const Vector3 oth); private: // 행렬 요소 float elements[16]; };
// Matrix4.cpp #include "Matrix4.h" #include <string> Matrix4::Matrix4() { memset(elements, 0, sizeof(elements)); } Matrix4 Matrix4::Identity() { Matrix4 ret; ret.Get(0, 0) = 1.0f; ret.Get(0, 1) = 0.0f; ret.Get(0, 2) = 0.0f; ret.Get(0, 3) = 0.0f; ret.Get(1, 0) = 0.0f; ret.Get(1, 1) = 1.0f; ret.Get(1, 2) = 0.0f; ret.Get(1, 3) = 0.0f; ret.Get(2, 0) = 0.0f; ret.Get(2, 1) = 0.0f; ret.Get(2, 2) = 1.0f; ret.Get(2, 3) = 0.0f; ret.Get(3, 0) = 0.0f; ret.Get(3, 1) = 0.0f; ret.Get(3, 2) = 0.0f; ret.Get(3, 3) = 1.0f; return ret; } Matrix4 Matrix4::Translation(Vector3 pos) { return Translation(pos.x, pos.y, pos.z); } Matrix4 Matrix4::Translation(float x, float y, float z) { Matrix4 ret; ret.Get(0, 0) = 1.0f; ret.Get(0, 1) = 0.0f; ret.Get(0, 2) = 0.0f; ret.Get(0, 3) = x; ret.Get(1, 0) = 0.0f; ret.Get(1, 1) = 1.0f; ret.Get(1, 2) = 0.0f; ret.Get(1, 3) = y; ret.Get(2, 0) = 0.0f; ret.Get(2, 1) = 0.0f; ret.Get(2, 2) = 1.0f; ret.Get(2, 3) = z; ret.Get(3, 0) = 0.0f; ret.Get(3, 1) = 0.0f; ret.Get(3, 2) = 0.0f; ret.Get(3, 3) = 1.0f; return ret; } Matrix4 Matrix4::InverseTranslation(Vector3 pos) { return InverseTranslation(pos.x, pos.y, pos.z); } Matrix4 Matrix4::InverseTranslation(float x, float y, float z) { Matrix4 ret; ret.Get(0, 0) = 1.0f; ret.Get(0, 1) = 0.0f; ret.Get(0, 2) = 0.0f; ret.Get(0, 3) = -x; ret.Get(1, 0) = 0.0f; ret.Get(1, 1) = 1.0f; ret.Get(1, 2) = 0.0f; ret.Get(1, 3) = -y; ret.Get(2, 0) = 0.0f; ret.Get(2, 1) = 0.0f; ret.Get(2, 2) = 1.0f; ret.Get(2, 3) = -z; ret.Get(3, 0) = 0.0f; ret.Get(3, 1) = 0.0f; ret.Get(3, 2) = 0.0f; ret.Get(3, 3) = 1.0f; return ret; } Matrix4 Matrix4::Rotation(Vector3 rot) { return Rotation(rot.x, rot.y, rot.z); } Matrix4 Matrix4::Rotation(float x, float y, float z) { return RotationY(y) * RotationX(x) * RotationZ(z); } Matrix4 Matrix4::InverseRotation(Vector3 rot) { return InverseRotation(rot.x, rot.y, rot.z); } Matrix4 Matrix4::InverseRotation(float x, float y, float z) { return Transpose(RotationZ(z)) * Transpose(RotationX(x)) * Transpose(RotationY(y)); } Matrix4 Matrix4::RotationX(float angle) { Matrix4 ret; angle *= Math::Deg2Rad; float cos = cosf(angle); float sin = sinf(angle); ret.Get(0, 0) = 1.0f; ret.Get(0, 1) = 0.0f; ret.Get(0, 2) = 0.0f; ret.Get(0, 3) = 0.0f; ret.Get(1, 0) = 0.0f; ret.Get(1, 1) = cos; ret.Get(1, 2) = -sin; ret.Get(1, 3) = 0.0f; ret.Get(2, 0) = 0.0f; ret.Get(2, 1) = sin; ret.Get(2, 2) = cos; ret.Get(2, 3) = 0.0f; ret.Get(3, 0) = 0.0f; ret.Get(3, 1) = 0.0f; ret.Get(3, 2) = 0.0f; ret.Get(3, 3) = 1.0f; return ret; } Matrix4 Matrix4::RotationY(float angle) { Matrix4 ret; angle *= Math::Deg2Rad; float cos = cosf(angle); float sin = sinf(angle); ret.Get(0, 0) = cos; ret.Get(0, 1) = 0.0f; ret.Get(0, 2) = sin; ret.Get(0, 3) = 0.0f; ret.Get(1, 0) = 0.0f; ret.Get(1, 1) = 1.0f; ret.Get(1, 2) = 0.0f; ret.Get(1, 3) = 0.0f; ret.Get(2, 0) = -sin; ret.Get(2, 1) = 0.0f; ret.Get(2, 2) = cos; ret.Get(2, 3) = 0.0f; ret.Get(3, 0) = 0.0f; ret.Get(3, 1) = 0.0f; ret.Get(3, 2) = 0.0f; ret.Get(3, 3) = 1.0f; return ret; } Matrix4 Matrix4::RotationZ(float angle) { Matrix4 ret; angle *= Math::Deg2Rad; float cos = cosf(angle); float sin = sinf(angle); ret.Get(0, 0) = cos; ret.Get(0, 1) = -sin; ret.Get(0, 2) = 0.0f; ret.Get(0, 3) = 0.0f; ret.Get(1, 0) = sin; ret.Get(1, 1) = cos; ret.Get(1, 2) = 0.0f; ret.Get(1, 3) = 0.0f; ret.Get(2, 0) = 0.0f; ret.Get(2, 1) = 0.0f; ret.Get(2, 2) = 1.0f; ret.Get(2, 3) = 0.0f; ret.Get(3, 0) = 0.0f; ret.Get(3, 1) = 0.0f; ret.Get(3, 2) = 0.0f; ret.Get(3, 3) = 1.0f; return ret; } Matrix4 Matrix4::Scale(Vector3 scale) { return Scale(scale.x, scale.y, scale.z); } Matrix4 Matrix4::Scale(float x, float y, float z) { Matrix4 ret; ret.Get(0, 0) = x; ret.Get(0, 1) = 0.0f; ret.Get(0, 2) = 0.0f; ret.Get(0, 3) = 0.0f; ret.Get(1, 0) = 0.0f; ret.Get(1, 1) = y; ret.Get(1, 2) = 0.0f; ret.Get(1, 3) = 0.0f; ret.Get(2, 0) = 0.0f; ret.Get(2, 1) = 0.0f; ret.Get(2, 2) = z; ret.Get(2, 3) = 0.0f; ret.Get(3, 0) = 0.0f; ret.Get(3, 1) = 0.0f; ret.Get(3, 2) = 0.0f; ret.Get(3, 3) = 1.0f; return ret; } Matrix4 Matrix4::ViewLookAt(Vector3 pos, Vector3 target, Vector3 up) { Vector3 f = (target - pos).Normalize(); Vector3 r = up.Cross(f).Normalize(); Vector3 u = f.Cross(r).Normalize(); Matrix4 ret; ret.Get(0, 0) = r.x; ret.Get(0, 1) = r.y; ret.Get(0, 2) = r.z; ret.Get(0, 3) = 0.0f; ret.Get(1, 0) = u.x; ret.Get(1, 1) = u.y; ret.Get(1, 2) = u.z; ret.Get(1, 3) = 0.0f; ret.Get(2, 0) = f.x; ret.Get(2, 1) = f.y; ret.Get(2, 2) = f.z; ret.Get(2, 3) = 0.0f; ret.Get(3, 0) = 0.0f; ret.Get(3, 1) = 0.0f; ret.Get(3, 2) = 0.0f; ret.Get(3, 3) = 1.0f; return ret * InverseTranslation(pos); } Matrix4 Matrix4::ViewEuler(Vector3 pos, float pitch, float yaw) { return InverseRotation(pitch, yaw, 0.0f) * InverseTranslation(pos); } Matrix4 Matrix4::ProjectionPerspective(float fov, float width, float height, float near, float far) { float aspectRatio = width / height; float H = 1 / tanf(fov / 2.0f); float W = H / aspectRatio; float A = far / (far - near); float B = (-near * far) / (far - near); Matrix4 ret; ret.Get(0, 0) = W; ret.Get(0, 1) = 0.0f; ret.Get(0, 2) = 0.0f; ret.Get(0, 3) = 0.0f; ret.Get(1, 0) = 0.0f; ret.Get(1, 1) = H; ret.Get(1, 2) = 0.0f; ret.Get(1, 3) = 0.0f; ret.Get(2, 0) = 0.0f; ret.Get(2, 1) = 0.0f; ret.Get(2, 2) = A; ret.Get(2, 3) = B; ret.Get(3, 0) = 0.0f; ret.Get(3, 1) = 0.0f; ret.Get(3, 2) = 1.0f; ret.Get(3, 3) = 0.0f; return ret; } Matrix4 Matrix4::ProjectionOrthographic(float width, float height, float near, float far) { float W = 2 / width; float H = 2 / height; float A = 1 / (far - near); float B = -near / (far - near); Matrix4 ret; ret.Get(0, 0) = W; ret.Get(0, 1) = 0.0f; ret.Get(0, 2) = 0.0f; ret.Get(0, 3) = 0.0f; ret.Get(1, 0) = 0.0f; ret.Get(1, 1) = H; ret.Get(1, 2) = 0.0f; ret.Get(1, 3) = 0.0f; ret.Get(2, 0) = 0.0f; ret.Get(2, 1) = 0.0f; ret.Get(2, 2) = A; ret.Get(2, 3) = B; ret.Get(3, 0) = 0.0f; ret.Get(3, 1) = 0.0f; ret.Get(3, 2) = 0.0f; ret.Get(3, 3) = 1.0f; return ret; } Matrix4 Matrix4::Transpose(const Matrix4& target) { Matrix4 ret = target; Math::Swap(ret.Get(0, 1), ret.Get(1, 0)); Math::Swap(ret.Get(0, 2), ret.Get(2, 0)); Math::Swap(ret.Get(0, 3), ret.Get(3, 0)); Math::Swap(ret.Get(1, 2), ret.Get(2, 1)); Math::Swap(ret.Get(1, 3), ret.Get(3, 1)); Math::Swap(ret.Get(2, 3), ret.Get(3, 2)); return ret; } float& Matrix4::Get(int row, int col) { return elements[(row * 4) + col]; } Matrix4& Matrix4::operator=(const Matrix4 oth) { for (int i = 0; i < 16; i++) { elements[i] = oth.elements[i]; } return *this; } Matrix4 Matrix4::operator*(const Matrix4 other) { Matrix4 ret; for (int i = 0; i < 4; i++) { Mul(ret, other, i, 0); Mul(ret, other, i, 1); Mul(ret, other, i, 2); Mul(ret, other, i, 3); } return ret; } void Matrix4::Mul(Matrix4& ret, Matrix4 other, int row, int col) { ret.Get(row, col) = Get(row, 0) * other.Get(0, col) + Get(row, 1) * other.Get(1, col) + Get(row, 2) * other.Get(2, col) + Get(row, 3) * other.Get(3, col); } Matrix4 Matrix4::operator*=(const Matrix4 other) { *this = *this * other; return *this; } Vector3 Matrix4::operator*(const Vector3 other) { Vector3 ret; ret.x = elements[0] * other.x + elements[1] * other.y + elements[2] * other.z; ret.y = elements[4] * other.x + elements[5] * other.y + elements[6] * other.z; ret.z = elements[8] * other.x + elements[9] * other.y + elements[10] * other.z; return ret; }
이렇게 해서 Matrix4 클래스의 구현이 끝났습니다.
행렬의 결과만 적으면 되기 때문에 그렇게 어렵진 않았습니다.
그럼 다음 글에서는 삼각형을 화면에 띄워보도록 하겠습니다.
읽어주셔서 감사합니다.
'Game Development > DirectX' 카테고리의 다른 글
[DirectX] 5. 텍스처 입히기 (1) 2024.09.08 [DirectX] 4. 삼각형 띄우기 (0) 2024.06.26 [DirectX] 3-3. View 행렬, Projection 행렬 (0) 2024.06.19 [DirectX] 3-2. World 행렬 (0) 2024.06.17 [DirectX] 3-1. 행렬 (0) 2024.06.17