행렬이란? |
행렬은 행과 열이 있는 격자모양 배열 안에 스칼라를 나열한 것이다. Matrix라는 말은 라틴어의 자궁에서 유래한다.
행렬 안에 나열된 스칼라를 요소(element)라 부른다.
정사각행렬(squre matrix)
행과 열의 수가 같은 행렬이라 한다. 게임에서 사용되는 행렬을 대부분 정사각 행렬이라 하고 특정 행이나 열이 항상 변홯지 않는 등의 최적화의 여지가 있을 경우에만 무시한다.
대각요소(diagonal elemnet)
행렬요소에 관하여 i=j인 요소를 가리킨다.
대각행렬(diagonal matrix)
정사각행렬이고 대각 요소 이외에 모두 0인 행렬
단위행렬(identity matrix)
대각행렬 중 대각요소가 모두 1
Matrix4x4클래스의 identity프로퍼티는 단위행렬이다.
영행렬(zero matrix, null matrix)
모든 요소가 0인 행렬
전치행렬(transpose)
행렬 M에서 i행j열과 j행 i열로 바꾸는것
단위행렬
역행렬(inverse)
행렬 M의 역행렬은 아래와 같이 표시한다.
행렬과 역행렬을 곱하면 단위행렬이 된다.
가역행렬(invertible matrix) 또는 정칙행렬(regular matrix)
모두 0이 들어 있는 행과 열을 포함하는 행렬이 존재하지 않으므로 역행렬이 존재하는 행렬을 칭함.
행렬의 덧셈 |
유니티에서 Matrix4x4 클래스에서는 두 행렬의 곱을 반환하는 operator*만 연산자로 정의되어 있음.
따라서 행렬끼리 곱하고자 한다면 직접 요소에 액세스 해서 수동으로 연산해야 함.
행렬의 곱셈 |
M = AB
결과인 행렬 M은 행수가 A의 행수, 열수가 B의 열수가 됩니다.
M의 요소는 다음과 같이 정의할 수 있습니다.
행렬의 곱에서는 교환법칙이 성립하지 않는다.
행렬의 곱에서는 결합법칙은 성립한다.
Transform으로 유니티 인스펙터 상으로는 따로따로 포함되어 있지만
행렬로 표현한다고 합니다.
행렬의 곱셈 방법은 다음과 같습니다.
이것을 어디에 사용하냐면 Transparent에 사용한다고함.
유니티는 3차원 벡터라서 열기준 4차원 행렬을 사용해야한다.
유니티에서 Position값은 아래와 같이 나타낼 수 있다.
DirectX는 행(row Major)기준
OpenGL은 열(Column major)기준
OpenGL을 사용하는 유니티는 열 기준 행렬(Column major)인데
첫번째 열 x축
두번째 열 y축
세번째 열 z축
네번째 열 Position
이게 어떻게 유니티에 적용되느냐 하는건 동영상 참조.--> 링크
행에 Vector값을 곱할때는 Vector가 앞에 오고 Matrix가 뒤에 온다.
V x M
열에 Vector값을 곱할때는 Vector가 뒤에오고 Matrix가 앞에 온다.
M x V
스위즐연산 |
행렬과 벡터의 곱으로 벡터의 성분을 변경하는 것을 말한다.
'스위즐(swizzle)'이란 '셔플'과 비슷하게 서로 뒤섞는다는 의미로, 여기서는 대상 벡터의 임의 의 성분을 임의의 순서로 뽑아내 나열하는 것을 의미한다.
행렬과 열벡터를 곱하면, 행렬의 각 행과 열벡터와의 내적이 결과 열벡터의 각 성분으로 들어온다. 다시 말해, 행렬의 각 열의 성분 중 원래 열벡터 안에서 원하는 성분에 대응하는 위치 성분이 1이고 다른 요소가 모두 0이라면, 원래 열벡터의 원하는 성분의 수치만 결과 열벡터의 성분으로 들어오게 된다.
▷부울 행렬(Boolean matrix)
벡터 v의 성분을 역순으로 얻고 싶을 때 사용
▷산포 스위즐(broadcast swizzle)
성분 하나를 벡터 전체에 벌여 놓는 방법이다.
유니티에는 구현되지 않았지만 CPU에 내장된 SIMD 명령세트와 GPU 쉐이딩언어에 구현된 경우가 많다.
행우선과 열우선 |
열 우선과 행 우선은 컴퓨터 구조에 따른 성질이지 수학에 개념은 아니다.
유니티의 Matrix4x4 클래스는 열우선(column major)이라고 되어 있다.
행 우선(row major) 방식에서는 행렬의 1행씩 1차원에 먼저 채워지는 것
열 우선(column major)방식에서는 행렬의 1열씩 1차원에 먼저 채워지는 방식이다.
4행 4열의 행일때
행우선 메모리배치 방식은 아래와같다
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
열우선 메모리 배치 방식은 같다.
1 5 9 13 2 6 10 14 3 7 11 15 4 8 12 16
유니티가 행우선 방식을 사용하는 이유는 행렬과 벡터의 내적으로 성분을 구한경우
각행에 액세스가 발생하므로 행우선으로 행렬이 들어있는 편이 엑세스하기 유리해진다.
1 5 9 13 x xyzw
...
4 8 12 16 x xyxw
이런식..
※스위즐 연산을 적용한 행벡터와 대응하는 위치의 행렬 M의 각 열벡터의 곱하는 아마다르곱(Hadamard product)에 대한 자료를 더 찾아봐야겠다..
AoS(Array of Structure) 와 SoA(Structure of Arrays) |
행렬을 행우선으로 유지할지 열우선으로 유지할지에 대한 메모리상의 레이아웃 방식이다.
C/C++과 C#의 구조체는 복수의 멤버 변수를 가진다.
이 멤버 변수를 x, y, z, w라 했을 때 이를 행우선으로 처리한다면 4가지 다른 종류의 데이터를 가진 구조체를 요소로 하는 배열의 요소를 차례로 처리해나가게 된다.
이것을 구조체의 배열(AoS)이라 한다.
구조체와 클래스를 정의하고, 그 안에 각각 의미가 있는 멤버변수를 두는 것이 캡슐화 등과 같은 인간이 쉽게 이해하는 것에 무게를 둔 객체지향 프로그래밍 모델이다.
AoS상태로 객체가 메모리에 존재할 경우 분산된 메모리 주소를 읽거나 사용하므로 메모리에 랜덤하게 액세스 하여 레이턴시가 발생하여 성능이 저하된다.
반면 열우선으로 처리해 나간다고 하면 각 성분에 같은 연산을 적용하여 일괄처리하므로 배열을 멤버로서 여러 개 묶은 구조체를 전체로본다. 이것이 배열의 구조체 (SoA)다.
최근의 CPU는 스트림 프로세싱이라 불리는 기법으로 대량의 데이터를 처리하기 위해 1명령으로 복수의 같은 종류의 데이터를 처리하는 SIMD 명령을 구현한다.
예를들어 같은 처리를 요하는 32비트 데이터를 4개 모아서, 128비트씩 모아서 한번에 적용할 수 있는 명령이다.
이런 명령은 같은 종류의 데이터를 복수동시병렬로 처리하는 벡터 연산 그 자체이므로 SoA형식을 사용한다.
단 SoA데이터에 랜덤액세스가 많이 발생하면 캐시 라인에 관련 데이터를 두기 어려워 공간적 소속성이 낮아지며 캐시스래싱(cache thrashing)이 일어나기 쉬워져 성능이 저하된다.
어쨋든 유니티는 CPU의 SIMD명령을 사용하는 API를 사용자에게 고애하지는 않는다. 따라서 Android나 iOS 디바이스에서 ARM NEON과 같은 SIMD 명령 세트를 이용해 연산을 처리하려면 유니티 스크립트가 아닌 네이티브 코드를 이용해 플러그인을 만들고 그 내부에서 intrinsic 함수군을 사요하거나 인라인 어셈블리를 작성하거나 혹은 컴파일러의 자동 벡터화를 사용해야 한다.
※ 캐시스래싱 : 캐시 내용의 교체가 빈번이 일어나 캐시를 사이에 두는 의미가 손상되는것
행렬식 |
행렬에는 행렬식이 정의되어, 행렬을 수식으로 간주해 스칼라 값을 얻을 수 있다.
단 행렬식을 정의할 수 있는 것은 정사각행렬뿐이다.
행렬식에는 다음과 같은 성질이 성립한다.
1. 두 개의 행 또는 열을 교환하면 부호가 반전된다.
2. 행렬의 어떤 행 또는 열을 상수배하면, 행렬식 자체가 같은 상수배가 된다. 다시 말해, 각 행 또는 열에 대해 행렬식은 선형이다.(다중선형성).
임의의 정사각행렬의 행렬시을 구하는 식으로는 여인수(cofactor)를 이요한 여인수전개라는 방법이 있다.
i행 j열의 요소를 aij로 하는 nxm 행렬 A에서 i행과 j열을 제거한 (n - 1) x (n - 1) 행렬의 행렬식을 소행렬식이라 한다.
기하학적 의미의 행렬식 |
이 2x2 행렬식은 (0,0), (a, b), (c, d ), (a+c, b+d)를 정점을 하는 평행사변형의 면적과 같다.
여기서 행렬식의 성질로서 두개의 행을 교환하면 부호가 반저된다는 특성이 있다.
A에서도 행을 교체하면 행렬식은 같은 음수가 되므로 행렬 안에서 행백터의 순서는 행렬식에서 의미가 있다.
이런식으로 3차월 행렬식도 표현이 가능한데
3x3 행렬식은 각 열 (또는 각 행)을 각각 a,b,c,라는 벡터의 성분으로 봤을 때 a, b, c에 으해 생기는 평육면체의 체적과 같다.
벡터 bc로 이루어지는 평행사변형의 면적은 b와 c의 외적에 의해 생기는 벡터는 위쪽을 향한다. 평행육면체의 높이는 |a|cosΘ 이므로 평행육면체의 체적은 (바닥면적) x (노피0로 다음과 같이 된다.
|a| |bxc| cosΘ 이 식은 a와 벡터 bxc와의 내적을 구하는 것이라 볼 수 있으므로, 구하는 것은 다음과 같다.
aㆍ(b x c) 이 식은 스칼라 삼중적(scalar triple product)이라 한다. 3장에서 다룬 벡터 삼중적에서는 벡터의 순서를 바꾸면 결과가 달라졌지만, 스칼라 삼중적에서는 벡터 a, b, c를 어누 순서로 어느 식에 끼워넣든 간에 평행 육면체의 체적이 변하지 않으므로 다ㅡㅁ과 같은 식이 성립한다.
aㆍ(b x c ) = bㆍ(c x a) = cㆍ(a x b)
스칼라 삼중적에 a, b, c의 성분 표시를 적용하여 계산하면, 결과는 각 열을 가각 a, b, c라는 벡터의 성분으로 3x3행렬식과 같아진다.
왼손 좌표계를 사용한 경우 b x c 는 역방향을 향하므로 cosΘ의 부호가 바뀌고 행렬식은 음수가 된다. 그런 읨에서 행렬식으로 얻는 부화 있는 체적이고, 유사벡터처럼 좌표게에 따라 부호를 바꾸므로 유사스칼라(pseudo scalar)라 불린다.
또한 a, b, c가 선형 독립이 아니라 선형 종속일 때 벡터들은 평면 위에 찌부러진 상태 또는 단순한 직선이 되어 체적이 있는 평행 육면체를 만들 수 없으므로 행렬식은 0이 된다. 다시 말해서 행렬식을 계산해서 0인지 아닌지를 살펴보면 a,b,c가 선형독립인지 선형종속인지 알 수 있다.
직교행렬 |
또한, 역행렬을 설명했을 때 가역행렬은 다음과 같은 성질을 만족한다.
따라서 직교행렬 M은 다음과 같다.
즉, 행렬이 직교행렬일 때는 단순히 전치하면 역행렬을 얻을 수 있다는 뜻이다. 직교행렬이 직교로 불리는 까닭은 어느 열베터 또는 행 벡터를 두 개 추출하더라도 두 개의 내적은 0이 된다.(=직교다)는 점에서 유래한다.
행렬식은 어떨가? 직교행렬의 행렬식은 1 또는 -1이다. 행렬 A, B를 행렬식의 성질로서 어미를 살펴본 것처럼, 다음과 같은 식이 성립한다.
다시 말하면 제곱해서 1이 된다는 것은 직교행렬의 행렬식1이 또는 -1이라는 말이 된다.
직교행렬 중, 행렬식 1이 되는 것을 특수직교행렬(special othogonal matrix)이라고 한다.
특수 직교행렬은 어떤 행과 열도 정규화된 크기 인 단위벡터로서 다룰수 잇는 성질을 가진다.
또한, 행기리, 열끼리는 서로 수직이다. 서로 수직인(직교한) 단위벡터라는 의미에서 특수직교행렬의 각 행, 각 열은 정규직교기저(orthonormal basis)라 불린다. 정규직교기저는 직교좌표계의 기저벡터다.
참조)
유니티로 배우는 게임 수학