[게임 수학] #8 | 행렬(Matrix)

2023. 11. 16. 22:20Computer Sciences/Game Mathemathics

인프런 <게임 엔진을 지탱하는 게임수학, 이득우 교수님> 강의를 듣고 공부한 글입니다.

 

 

1. 행렬(Matrix)

벡터가 하나의 행 또는 열만을 표현할 수 있는 것에 비해, 행렬은 행 벡터(Row vector) 혹은 열 벡터(Column vector)들을 활용하여 2차원으로 구성이 가능합니다. 이러한 행렬은 컴퓨터 그래픽스에서 점이나 오브젝트 등을 다른 위치로 옮기거나 회전하는 등의 변환 연산에 주로 사용됩니다.

 

행렬이란 것은 단순하게 정의하면, 어떤 사각형 틀 안에 행과 열을 맞춰서 수를 나열한 것에 불과합니다. 다시 말해, 가로를 행(Row), 세로를 열(Column)이라고 하는 특정한 사각형 틀에 스칼라 값들을 나열하는 것이죠. 예를 들어, 2×3A라는 이름의 행렬을 다음과 같이 표기할 수 있습니다.

 

A=[a11a12a13a21a22a23]

 

이렇게 표현하는 행렬 자체에 무언가 특별한 의미가 있다기 보다는, 편리하게 계산할 수 있도록 배치한 것이라고 보면 되겠습니다.

 

 

행렬의 연산

행렬과 행렬의 덧셈

크기가 같은(행과 열의 개수가 같은) 행렬에만 적용이 가능하며, 각 원소 위치에 해당하는 값들을 서로 더하고 동일한 위치에 지정해주면 됩니다.

 

A+B=[abcd]+[efgh]=[a+eb+fc+gd+h]

 

행렬과 스칼라의 곱셈

모든 행렬 원소에다가 스칼라 값을 곱해주는 연산을 수행해주면 됩니다.

 

kA=k[abcd]=[kakbkckd]

 

행렬의 전치(Transpose) 연산

전치(Transpose)행과 열을 바꿔치기 하는 연산을 의미합니다. 즉, 행이 열이 되고, 열이 행이 되는 것이죠.

 

[adbecf]T=[abcdef]

 

행렬과 행렬의 곱셈

두 행렬의 곱셈 A×B를 수행하려면, "앞에 있는 행렬(A)의 열 개수 = 뒤에 있는 행렬(B)의 행 개수" 여야 합니다. A(m×n)B(n×p) 이런 경우에만 곱셈이 가능하다는 말이죠. 이렇게 곱셈을 해서 나오는 행렬 CA 행렬의 행 개수인 mB 행렬의 열 개수인 p만큼의 사이즈인 m×p의 크기가 됩니다.

 

C(m×p)=A(m×n)B(n×p)

 

 

곱셈을 하는 방법은 조금 복잡합니다. 앞 행렬은 행 벡터(가로 방향) 순서대로, 뒷 행렬은 열 벡터(세로 방향) 순서대로 각 원소를 곱해 더하면 됩니다.

 

AB=[abcd][efgh]=[ae+bgaf+bhce+dgcf+dh]

 

 

이러한 곱셈 연산과 관련하여 중요하게 알아야 할 사실이 있습니다.

 

교환 법칙을 만족하지 않는다.

 

AB=[abcd][efgh]=[ae+bgaf+bhce+dgcf+dh]

 

BA=[efgh][abcd]=[ae+cfbe+dfag+chbg+dh]

 

ABBA

 

결합 법칙은 성립한다.

 

A(BC)=(AB)C

 

즉, 무엇을 먼저 곱하든 간에 기존의 순서만 유지된다면 동일한 결과를 보장한다는 것이죠.

이러한 행렬의 성질은 아주 큰 이점을 가지게 됩니다. 그 내용은 뒤에서 보도록 하죠.

 

전치 연산

 

(AB)T=BTAT

 

(AB)T=[ae+bgaf+bhce+dgcf+dh]T=[ae+bgce+dgaf+bhcf+dh]

이것은 다음과 같습니다.

BTAT=[egfh][acbd]=[ae+bgce+dgaf+bgcf+dh]

 

분배 법칙

 

행렬의 덧셈과 곱셈을 사용한 분배 법칙도 만족합니다.

 

A(B+C)=AB+AC

(B+C)A=BA+CA

 

다만, 위에서 보이는 것처럼 행렬 A가 좌측에서 분배된다면, 각 행렬의 곱셈 결과에는 A가 좌측에 있어야 합니다. 반대로, 행렬 A가 우측에서 분배된다면, 각 행렬의 곱셈 결과에 A가 우측에 있어야 합니다. 이것 역시 교환 법칙이 성립하지 않는 행렬의 특성에서 나온 주의점인 것 같네요.

 

 

 


2. 선형 변환과 행렬

벡터 공간의 구조를 그대로 유지하면서 선형성을 띄는 선형 변환을 통해, 새로운 공간으로 변환되는 그 과정 자체가 행렬에 대응된다는 점이 있고, 이것이 행렬을 사용하는 이유입니다. 1)복잡한 선형 변환식을 일일이 계산하지 않아도 되며, 2)행렬로 정리된 간편한 식의 곱셈 전개를 통해 원하는 선형 변환을 빠르게 구현할 수 있다는 장점이 있습니다.

 

앞 전에 선형성 파트에서, R2R2 대응관계를 가지는 다음 벡터 함수는 선형성을 만족한다는 것을 알아봤습니다.

 

f((x,y))=(ax+by,cx+dy)

 

위와 같은 선형 변환식은 어떻게 행렬식으로 바꿀 수 있을까요? 어떤 임의의 행렬 A

 

[abcd]

 

라고 정의하고, 임의의 벡터 v

 

[xy]

 

를 정의했을 때, 행렬과 행렬의 곰셉 법칙에 의해서 다음과 같은 결과가 나옵니다.

 

Av=[abcd][xy]=[ax+bycx+dy]

 

 

위 식을 보면, 선형 변환식에서 원래 사용되었던 x,y열 벡터 v 로 존재하고, ax+by 를 첫 번째 요소로, cd+dy 를 두 번째 요소로 가지는 결과물이 만들어지게 됩니다.

 

그렇다면, 위 식에서 곱했던 행렬 A는 무엇일까요? 바로, (ax+by, cx+dy) 이라고 하는 어떤 점으로 대응시켜주는 선형 변환 함수의 역할을 하는 것입니다. 이러한 결과들을 통해, 우리는 다음과 같은 의미를 추론할 수 있습니다.

  • A 행렬은 같은 차원의 공간이 서로 대응되도록 하는 선형 변환
    • 즉, 정방 행렬은 같은 차원의 공간이 서로 대응되도록 하는 선형 변환임을 의미
    • A 행렬은 2×2 정방 행렬이었으므로, R2R2 
  • 열 벡터는 벡터 공간의 벡터

 

열 기반 행렬과 행 기반 행렬

벡터를 행으로 표현하냐, 열로 표현하냐에 따라 방법이 두 가지가 있습니다. 열 기반 행렬(Column Major Matrix)는 수학에서 사용하는 기본 방식이며, OpenGL에서 사용하고 있습니다. 벡터의 순서가 뒤에 온다는 특징이 있으며, 결과 또한 열의 결과로 나오는 걸 볼 수 있습니다.

 

[abcd][xy]=[ax+bycx+dy]

 

 

행 기반 행렬(Row Major Matrix)는 DirectX 및 게임 엔진에서 사용하는 방식입니다. 벡터의 순서가 앞에 온다는 특징이 있으며, 결과 또한 행의 결과로 나오는 걸 볼 수 있습니다.

 

[xy][acbd]=[ax+bycx+dy]

 

 

허나, 사실상 열 기반 행렬과 행 기반 행렬은 서로 전치 관계일 뿐, 결과는 동일함을 보장합니다.

 

Av=v

(v)T=(Av)T=vTAT

 

 


3. 선형 변환의 시각화

변환되기 전의 어떠한 벡터 공간 (x,y) 가 있다고 가정해 봅시다. 여기에서 x, y 는 서로 균일하게 동등한 관계에서 똑같이 간섭 없이 조합됐다고 볼 수 있습니다. 즉, 선형 독립인 거죠.

 

그렇기에, 이러한 임의의 점 (x,y) 를 어떻게 생성했는지 구조를 생각해 본다면, 표준 기저 벡터 e1(1,0)e2(0,1) 에다가 각각 x 배, y 해서 더했다고 볼 수 있습니다. 선형 변환을 하기 전, 원래 벡터 공간에 속한 임의의 벡터에 대한 조합식이 다음과 같다고 생각해 봅시다.

 

(x,y)=x(1,0)+y(0,1)

 

그리고 선형 변환 함수 역할을 하는 다음과 같은 A 행렬이 있다고 합시다.

 

A=[abcd]

 

이 때, A 행렬을 각 열 벡터로 나누어 보게 된다면 (a,c), (b,d) 가 되고, 이것은 변화된 공간의 표준 기저 벡터 e1(a,c),e2(b,d) 를 의미합니다. 이 말은 기존의 벡터 공간을 이루고 있던 표준 기저 벡터 e1(1,0), e2(0,1)A 행렬의 각 열 벡터 성분으로 변화되었다라고 해석할 수 있겠지요.

 

x(1,0)+y(0,1)x(a,c)+y(b,d)

 

([왼쪽] 변환 전 공간, [오른쪽] 변환 후 공간

 

이렇게 선형 변환된 기저 벡터를 조합해, 변화된 공간의 모든 벡터들을 일일이 추적하지 않아도 알아낼 수 있게 됩니다.

 

(x,y)=x(a,c)+y(b,d)=(ax+by,cx+dy)

 

그런데, 이 식 어디서 많이 보지 않았나요?

 

f((x,y))=(ax+by,cx+dy)

 

네. 앞서 살펴 봤었던 선형 변환식과 똑같다는 걸 알 수 있습니다. 이런 식으로 표준 기저 벡터를 변환할 수 있다면, 원하는 변환을 쉽게 적용할 수 있게 됩니다.

 

 

예) 크기 변환 행렬

다음과 같은 원래 평면 공간에 존재하는 물체를 좌우로는 A 만큼 늘리고, 상하로는 B 만큼 줄이려고 합니다. 그러면, 기존 평면을 이루던 공간의 표준 기저 벡터 e1(1,0), e2(0,1) 에 변환시켜 줄 행렬을 적용해야 겠지요. 가로로는 A 만큼 늘리고, 세로로는 B 만큼 줄인다고 했으니까 다음과 같은 행렬을 사용하면 될 겁니다.

 

[a00b][xy]=[ax+by]

 

[왼쪽] 원래 평면 표준 공간 [오른쪽] 크기 변환을 적용한 결과

 

 

예) 밀기 변환

이번에는 y축만 x축 방향으로 a 칸 미는 변환을 해봅시다. x축은 변화가 없으니 (1, 0) 그대로 사용해주면 될 것이고, 대신 y축이 기존 위치에서 a만큼 오른쪽으로 밀어지는 거니 (a,1)을 사용하면 됩니다.

 

[1a01][xy]=[x+ayy]

 

[왼쪽] 원래 평면 표준 공간 [오른쪽] 밀기 변환을 적용한 결과

 

예) 임의의 각 θ에 대한 회전

표준 평면 공간에서 임의의 각 θ 만큼 회전을 하게 되면, 표준 기저 벡터 e1, e2 는 각각 다음과 같이 좌표가 변화하게 됩니다.

 

[왼쪽] 원래 평면 표준 공간 [오른쪽] 회전한 결과

 

e1(cosθ, sinθ) 로 변화해야 하고, e2(sinθ, cosθ) 로 변화해야 하니, 다음과 같이 적용해주면 됩니다.

 

[cosθsinθsinθcosθ][xy]=[xcosθysinθxsinθ+ycosθ]

 

 


4. 행렬의 곱이 가지는 특징

행렬 하나의 선형 변환에 대응되며, 행렬의 곱은 선형 변환을 적용한 결과에 다시 선형 변환을 적용하는 과정을 거쳐 공간을 변환시키게 됩니다.

 

 

 

이를 수식으로 나타내면 다음과 같습니다.

 

W=A(Bv)

 

그런데, 행렬은 결합 법칙이 성립하므로 위 식은 다음과 같이 쓸 수 있습니다.

 

W=(AB)v

 

즉, 기존 벡터 공간 v에 변환 행렬 AB를 곱한 결괏값을 v와 곱하게 되면, 바로 변환된 벡터 공간 W를 얻을 수 있다는 의미가 되죠. 즉, 합성함수처럼 한 방에 중간 단계를 거치지 않고, 한 방에 건너뛰기가 된다는 소리입니다.

 

이러한 의미를 가지기에, 두 행렬을 곱한 식 AB는 벡터 공간 V에서 벡터 공간 W로 직행할 수 있게 해주는 변환 함수의 의미를 가지게 된다고 볼 수 있습니다.

 

 

 

이러한 특징은 계산 과정을 많이 줄여주는 효과를 가져올 수 있기 때문에, 게임에서 행렬이 필수적으로 쓰이는 것입니다. 예를 들어, 게임 개발을 하다 보면 다음과 같은 5가지 변환을 주로 하게 됩니다.

  • 크기 (S, Scale)
  • 회전 (R, Rotation)
  • 이동 (T, Translation)
  • 뷰 (V, View)
  • 투영 (P, Projection)

 

만약 어떠한 캐릭터가 10만 개의 점으로 이루어져 있다고 한다면, 이를 연산하여 모니터에 보여주기까지 위의 5가지 과정을 거치기에 50만번의 연산이 발생하게 됩니다.

 

P(V(T(R(Sv))))

 

그런데, 위와 같은 5가지 변환들이 항상 고정되어 있다면, PVTRS를 미리 계산해 행렬로 생성한 후, 이를 사용하면 동일한 결과를 만들어주는 연산이 되므로 10만번으로 해결할 수 있게 됩니다.

 

P(V(T(R(Sv))))=(PVTRS)v

 

 

지금까지 행렬에 대해 알아보는 시간을 가졌습니다.

728x90
반응형