2023. 12. 5. 15:55ㆍComputer Sciences/Game Mathemathics
*인프런 <게임 엔진을 지탱하는 게임수학, 이득우 교수님> 강의를 듣고 공부한 글입니다.
1. 벡터의 외적(Cross Product)
벡터의 외적 연산은 3차원 공간에서만 사용 가능한 연산으로, 스칼라 값을 결과로 내뱉었던 내적과 달리, 외적은 3차원 벡터를 결과로 배출합니다. 이러한 벡터의 외적 연산은 다른 축 요소들로만 연산이 이루어진다는 특징이 있습니다. 다음과 같이 3차원 벡터 \(u, \ v\) 가 있다고 하면,
$$ u = (u_x, \ u_y, \ u_z), \ \ v = (v_x, \ v_y, \ v_z) $$
이 두 벡터에 대한 외적 연산 결과는 다음과 같습니다.
$$ u \times v = (u_y v_z - v_y u_z, \ u_z v_x - v_z u_x, \ u_x v_y - v_x u_y)$$
위 식을 보면, \( (x, \ y, \ z)\) 에서 각 축마다 자기 자신을 제외한 연산이 이루어진다는 것을 알 수 있습니다. \(x\) 축 자리에 대한 값을 보면 \(u_y v_z - v_y u_z\) 로, \(u_x\) 부분이 없는 것을 확인할 수 있습니다. 자기 자신에 대한 축 부분만 연산에 사용했던 내적과의 차이점이라고 볼 수 있죠.
이런 특징 때문에, 외적은 내적이 가지는 연산의 부족한 부분을 보충해주는 연산이라고 할 수 있습니다.
2. 외적의 성질
1) 교환 법칙 👉🏻 성립하지 않음
교환 법칙이 성립했던 내적과 달리, 외적은 교환 법칙이 성립하지 않습니다. 외적 연산에는 위에서 식을 봤다시피, 뺄셈 연산자가 존재하기 때문이죠. 뺄셈은 순서가 바뀌면 결과도 달라집니다.
$$ u \times v \ne v \times u $$
하지만 뺄셈 연산이 있다는 것을 고려해, 다음과 같이 마이너스(\(-\)) 부호를 붙인 채로 자리를 바꾸는 것은 동일한 결과를 보장합니다.
$$ u \times v = -v \times u $$
2) 결합 법칙 👉🏻 성립하지 않음
외적 역시, 내적과 마찬가지로 결합 법칙이 성립하지 않습니다.
$$ (a \times b) \times c \ne a \times (b \times c) $$
3) 분배 법칙 👉🏻 성립
내적과 마찬가지로, 외적도 덧셈 연산에 대해 분배 법칙이 성립합니다.
$$ a \times (b + c) = a \times b \times c $$
4) 영벡터
평행한 벡터(같은 방향 혹은 반대 방향)와 외적하면 영벡터(Zero vector)가 나옵니다.
$$ u\times u=\left(u_yu_z-u_yu_z,u_zu_x-u_zu_x,u_xu_y-u_xu_y\right)=(0, \ 0, \ 0) $$
$$ u\times -u=\left(-u_yu_z+u_yu_z,-u_zu_x+u_zu_x,-u_xu_y+u_xu_y\right)=(0, \ 0, \ 0) $$
이러한 성질을 활용해, 임의의 벡터 \(u\) 를 수평 성분(\(u_{||}\))과 수직 성분(\( u_{\perp}\))으로 분리할 수 있습니다.
$$ u = u_{||} + u_{\perp} $$
이때 외적은 분배 법칙이 성립하기 때문에, 평행한 부분은 영벡터로 날아가고 수직인 부분만 남게 됩니다.
$$ v \times u = v(u_{||} + u_{\perp}) $$
$$ = v \times u_{||} + v \times u_{\perp} $$
$$ = (0, \ 0, \ 0) + v \times u_{\perp} $$
$$ = v \times u_{\perp} $$
결국 두 벡터의 외적 결과는 두 벡터가 서로 얼마나 수직이냐에 따라 크기가 결정된다는 것인데요. 이는 외적한 벡터의 크기가 사잇각의 \(sin\) 함수에 비례한다고 이야기 할 수 있습니다.
$$ |u_{\perp}| = sin\theta |u| $$
5) 임의의 두 벡터에 대한 외적
임의의 두 벡터 \( u = (u_x, \ u_y, \ u_z), \ v = (v_x, \ v_y, \ v_z) \) 에 대해, 외적한 결과는 다음과 같았습니다.
$$ u \times v = (u_y v_z - v_yu_z, \ u_z v_x - v_z u_x, \ u_x v_y - v_x u_y) $$
이때, 이 외적한 결과 벡터에 \(u\) 또는 \(v\) 를 내적하면 항상 0이 나오는 특징이 있습니다.
$$ u\cdot\left(u\times v\right)=u_xu_yv_z-u_xv_yu_z+\ u_yu_zv_x-u_yv_zu_x+u_zu_xv_y-u_zv_xu_y=0 $$
$$ v\cdot\left(u\times v\right)=v_xu_yv_z-v_xv_yu_z+\ v_yu_zv_x-v_yv_zu_x+v_zu_xv_y-v_zv_xu_y=0 $$
내적 결과가 0이라는 것은 두 벡터가 서로 직교한다는 의미입니다. 내적은 \(cos\) 함수에 비례하고, \(cos (90) = 0\) 이기 때문이죠. 그럼 다시 위 식의 의미를 해석해 봅시다.
"\(u \times v \) 벡터와 \(u\) 혹은 \(v\) 와의 내적 결과는 0이다."
👉🏻 "\(u \times v\) 는 \(u\) 와 \(v\) 와 직교한다."
만약 \(u, \ v\) 가 서로 다른 기울기의 벡터였다면, 이 두 벡터를 통해 평면을 생성될 겁니다. 여기에서 \(u \times v\) 를 진행하면 이 평면에 직교하는 벡터가 생성된다는 의미죠.
이를 통해, 외적으로 생성된 벡터는 두 벡터가 만든 평면에 항상 직교한다는 것을 알 수 있습니다. 이렇게, 두 벡터의 외적으로 만들어지는 벡터를 법선 벡터(Normal vector)라고 부릅니다.
그런데, 평면에는 앞과 뒤 이렇게 2개의 면이 존재하잖아요? 즉, 법선 벡터는 2개가 존재한다는 거죠.
법선 벡터의 방향은 오른손 법칙을 따라 만들어집니다. 오른손을 들고, 네 손가락을 감을 때 향하는 엄지 손가락 방향이 바로 법선 벡터의 방향인 것이죠.
위의 그림을 살펴보면 \(u \rightarrow v\) 로 네 손가락을 감을 때, 엄지는 위쪽을 향합니다. 👉🏻 \( u \times v \)
반대로, \(v \rightarrow u\) 로 네 손가락을 감을 때, 엄지는 아래를 향합니다. 👉🏻 \(v \times u \)
이와 같은 순서로 외적하여 평면에서 생성된 2개의 법선 벡터를 구분할 수 있고, 이것이 곧 평면의 방향이 됩니다.
내적과 외적 비교
외적은 \(sin\) 함수에, 내적은 \(cos\) 함수에 비례한다고 하였습니다. 이에 따라, 양수와 음수의 영역으로 나눌 수 있는데요.
- 외적은 \(sin\) 함수의 영향을 받기 때문에 180도를 기준으로 오른쪽이 양수, 왼쪽은 음수의 영역
- 내적은 \(cos\) 함수의 영향을 받기 때문에 90도, 270(-90)도를 기준으로 앞이 양수, 뒤가 음수의 영역
이와 같은 내용들, 그리고 내적의 값이 0일 때는 두 벡터가 서로 직교한다는 사실과 평행한 다른 벡터와 외적한 결과가 영벡터가 된다는 점에서 비롯하여, 내적과 외적을 다음과 같이 활용할 수 있습니다.
- 게임에서 적이 앞에 있는지, 뒤에 있는지 판별하는 로직에서 내적 활용
- 게임에서 적이 왼쪽에 있는지, 오른쪽에 있는지 판별하는 로직에서 외적 활용
- 내적은 직교 판별, 외적은 평행 판별에 활용
- 내적은 투영 벡터 생성에 활용, 외적은 법선 벡터 생성에 활용
3. 외적의 응용
1) 좌우 판별
게임 공간에 캐릭터와 어떠한 사물이 있다고 가정합시다. 이때, 캐릭터의 정면 시선 벡터를 \(f\) 라고 하고, 캐릭터에서 사물로 향하는 벡터를 \(v\) 라고 할 때, 다음과 같이 표현할 수 있습니다.
그러면 여기서, 벡터 \(f\) 와 벡터 \(v\) 를 외적하면 다음과 같이 윗방향 혹은 아랫방향의 법선 벡터가 생성될 겁니다.
사물이 캐릭터보다 오른쪽에 있다면(\(f \rightarrow v \)) 오른손 법칙에 의해 아래로 향하는 법선 벡터가 만들어질 것이고, 사물이 캐릭터보다 왼쪽에 있다면(\(f \rightarrow v'\)) 위로 향하는 법선 벡터가 만들어 질 겁니다.
하지만 외적의 결과는 벡터이므로, 이것만으로는 좌우 판별이 어렵습니다. 그러면 이 법선 벡터를 가지고 사물이 캐릭터의 왼쪽에 있는지 오른쪽에 있는지 어떻게 구분할 수 있을까요?
해답은 내적입니다. 월드 공간의 up 벡터(\( Y = (0, \ 1, \ 0)\)) 와 해당 법선 벡터를 내적하는 것이죠.
- up 벡터와 법선 벡터가 동일한 방향 👉🏻 두 벡터 사이의 각이 0도이므로 결과는 양수 👉🏻 사물이 왼쪽에 있음
- up 벡터와 법선 벡터가 반대 방향 👉🏻 두 벡터 사이의 각이 180도이므로 결과는 음수 👉🏻 사물이 오른쪽에 있음
이렇게 내적까지 추가로 진행한 이를 스칼라 삼중곱이라고도 표현합니다.
- [왼쪽에 있는 경우] \((f \times v) \cdot Y > 0 \)
- [오른쪽에 있는 경우] \((f \times v) \cdot Y < 0 \)
2) 벡터로부터 회전 행렬 생성
어떤 물체의 시선 방향이나 카메라의 시선 방향이 주어졌을 때, 해당 시선 방향으로부터 회전 행렬을 구하는 데에도 외적이 활용됩니다. 다음과 같이 카메라가 물체를 바라보고 있는 상황이라고 가정해보죠.
여기서 카메라의 \(z\) 로컬 축은 "사물의 위치" - "카메라의 위치"를 해주면 됩니다. 이렇게 구한 벡터의 크기가 1이 아닐 수 있으니, 정규화하는 과정을 진행해줍니다.
카메라의 \(x\) 로컬축은 방금 구한 카메라의 \(z\) 로컬축과 월드 공간의 up 벡터(\(Y_{world}\))를 외적하여 만들 수 있습니다. 이렇게 구한 \(x\) 로컬축과 \(Y_{world}\) 가 서로 직교한다고 보장은 할 수 없기에 정규화하여 크기를 1로 만들어줍니다.
마지막으로 카메라의 \(y\) 로컬축은 위에서 구한 \(z\) 로컬축과 \(x\) 로컬축을 외적함으로써 구할 수 있습니다. 이렇게 만들어진 \(y\) 로컬축은 \(z\) 로컬축과 \(x\) 로컬축에 서로 직교한다는 것을 보장받을 수 있어, 별도의 정규화는 하지 않아도 됩니다.
이 때까지의 과정을 수식으로 정리하면 다음과 같습니다.
$$ x_{local}=\frac{Y \times z_{local} }{|Y \times z_{local}|} $$
$$ y_{local}=z_{local} \times x_{local} $$
$$ z_{local}=\frac{v}{|v|} $$
이로부터 회전 행렬을 다음과 같이 구할 수 있습니다.
$$ R=\left[\begin{matrix}x_x&y_x&z_x&0\\x_y&y_y&z_y&0\\x_z&y_z&z_z&0\\0&0&0&1\\\end{matrix}\right] $$
주의사항
다음과 같은 특수 예외 상황이 발생하는 경우도 존재합니다.
- 카메라가 거꾸로 뒤집어져 있는 경우
- 카메라와 월드 up 벡터(\(Y_{world}\))가 평행한 경우
1번 같은 경우에는 up 벡터가 어디 방향을 향하는지 지정하는 옵션이 필요(사실 무슨 소리인지 잘 모르겠음)하고, 2번 같은 경우에는 up 벡터로 사용할 시선 방향과 직교하는 하나의 벡터를 임의로 지정해줘야 합니다.
3) 백페이스 컬링(Backface culling)
카메라에서 보이지 않는 뒷면의 삼각형을 그리지 않는 기법입니다. 삼각형도 결국에는 두 개의 면을 가지고 있기에, 카메라에 보이는 면과 안 보이는 면이 존재할 겁니다. 이때, 전면을 향하는 삼각형이 후면을 향하는 삼각형을 가리고 있다면, 이는 우리 입장에서 어차피 보이지 않는 영역입니다. 이에 따라, 가려지는 삼각형을 그리지 않음으로써 성능 향상을 도모하는 방법이라고 이야기 할 수 있겠습니다.
하지만 백페이스 컬링을 구현하기 위해, 메시 구조에 면 방향 정보를 추가하는 것은 공간 낭비가 심할 겁니다. 메시 구조에 추가적인 정보 저장없이, 백페이스 컬링을 구현할 방법이 필요한데, 이때에도 외적을 사용할 수 있습니다.
그리고 정점이 배열된 순서를 활용하여 해당 삼각형의 면의 방향(외적 결과)를 만들 수 있죠.
오른손 법칙에 따라, 시계 방향이면 후면 법선 벡터가 생성될 거고, 반시계 방향이면 전면 법선 벡터가 생성됩니다. 이러한 특성을 활용하여, 물체를 이루는 각 삼각형의 면의 방향 정보를 위와 같이 정점을 그리는 순서로 저장할 수 있게 되었지요.
그럼 이제 각 삼각형마다 법선 벡터 정보를 가지고 있는 셈이 되었으니, 이 법선 벡터와 카메라의 정면 시선 벡터를 내적해주면 됩니다. 같은 방향을 바라보고 있다면 양수가 나올 거고, 서로 마주보고 있다면 음수가 내적 결과로 나오겠죠.
이러한 방법으로 백페이스 컬링을 구현할 수 있습니다.
'Computer Sciences > Game Mathemathics' 카테고리의 다른 글
[게임 수학] #21 | 깊이 버퍼(Depth Buffer) (2) | 2023.12.07 |
---|---|
[게임 수학] #20 | 원근 투영(Perspective Projection) (2) | 2023.12.06 |
[게임 수학] #18 | 오일러 각(Euler angle) (2) | 2023.12.04 |
[게임 수학] # 17 | 3차원 공간 (4) | 2023.11.30 |
[게임 수학] #16 | 뷰 공간(View Space) (2) | 2023.11.29 |