2023. 12. 4. 13:09ㆍComputer Sciences/Game Mathemathics
*인프런 <게임 엔진을 지탱하는 게임수학, 이득우 교수님> 강의를 듣고 공부한 글입니다.
1. 회전 행렬 구현에서의 문제점
3차원 공간에는 세 개의 표준 기저 벡터 \(e_1\), \(e_2\), \(e_3\) 가 존재합니다. 이 세 개의 기저 벡터를 통해 회전을 구현하려면, 서로 직교하고 있던 원래 상태를 그대로 유지한 채로 돌아가야 하고, 변화된 기저 벡터들이 가지고 있는 값(\(x_{local}, \ y_{local}, \ z_{local}\)) 을 통해 회전 행렬을 만들 수 있습니다.
그런데 이 방법은 불편한 점이 있습니다. 예를 들어, 우리가 어떤 물체를 Y축으로 30도 회전하고 싶다고 할 때, Y축으로 30도 회전했을 때의 각 로컬축의 값이 얼마인지 구해야 회전이 가능하다는 이야기가 됩니다. 나는 30도 회전한 결과를 얻고 싶은데, 그러기 위해서는 30도를 회전했을 때의 값을 구해야 하는 상황인 셈이지요. 이것은 개발자 입장에서 매우 불편한 방식이 되겠지요.
또한, 행렬로 관리하기 때문에 9개의 데이터가 요구되어 계산량이 많다는 단점도 존재합니다. 이러한 단점들로 인해 좀 더 직관적이고 효율적인 방법이 필요해졌는데, 이때 많이 사용하는 것이 오일러 각(Euler Angle) 방식입니다.
2. 오일러 각(Euler Angle) 표현 방식
오일러 각 표현 방식은 3차원 공간에서 \(x, \ y, \ z\) 로컬축을 중심으로 회전한 수치값을 기입하는 방식입니다.
3차원 공간을 이루는 세 기저 축 \(e_1(1, \ 0, \ 0), \ e_2(0, \ 1, \ 0), \ e_3(0, \ 0, \ 1)\) 은 당연한 정보들이라 사실상 필요가 없습니다. 중요한 것은 각 축마다 얼만큼씩 회전을 시켰는지에 대한 양이지요. 이러한 부분에서 비롯되어, 순서대로 돌린 각도의 정보만 저장하면 3차원 공간에서의 회전을 다음과 같은 3개의 숫자로 표현할 수 있다는 것이 바로 오일러 각 표현 방식입니다.
$$ R = (\theta_x, \ \theta_y, \ \theta_z) $$
1) Yaw, Roll, Pitch
유니티 엔진에서는 Vector3(\(x, \ y, \ z\))로 관리하고, 언리얼 엔진에서는 Rotator라는 구조체에서 yaw, roll, pitch라는 값으로 관리하고 있습니다. 유니티 엔진 방식은 좀 더 직관적이라 바로 이해가 가능하지만, 언리얼 엔진에서 말하는 yaw, roll, pitch 라는 것은 무엇을 말하는 걸까요?
yaw, roll, pitch 는 비행기가 회전하는 모습을 토대로 설계된 회전 방식입니다. 항공학에서 시작해, 지금은 각종 기계 공학 및 게임에서 공통적으로 사용하고 있는 회전 방식이죠. 이러한 공통된 회전 방식을 사용함으로써, 축이 서로 다른 프로그램들 간에 회전 인터페이스를 제시할 수 있습니다.
이러한 방식의 회전은 정면에서 해당 물체를 봤을 때를 기준으로 하여, 다음과 같이 묘사할 수 있습니다.
- Yaw 회전 👉🏻 헬리콥터 프로펠러 회전
- Roll 회전 👉🏻 정면에서 원을 그리는 회전
- Pitch 회전 👉🏻 고개를 끄덕이는 방향의 회전
2) 오일러 각의 적용 순서
이제 회전 방향에 대한 공통 표준은 잡았으니, 이걸 어떻게 적용할 건지에 대해 살펴봐야 하는데요. 그 전에 알아야 할 점이 오일러 각은 한 번에 일어나는 회전이 아닌, 각 표준 기저 벡터 축을 중심으로 독립적으로 일어나는 회전이란 점입니다. 즉, 각 축마다 회전을 한 번씩 순서대로 적용하여 최종 회전 결과물을 낸다는 이야기죠.
$$ R = R_1 \cdot R_2 \cdot R_3 $$
이에 따라, 3가지 축에 대해 어떠한 순서로 회전을 적용할 지에 따라 다음과 같은 6가지 경우의 수가 생깁니다.
- Yaw → Pitch → Roll
- Yaw → Roll → Pitch
- Pitch → Roll → Yaw
- Pitch → Yaw → Roll
- Roll → Yaw → Pitch
- Roll → Pitch → Yaw
이 중에서 유니티 엔진과 언리얼 엔진뿐만 아니라, 기타 대부분의 엔진에서 채택한 순서는 다음과 같습니다.
*왜 그런지 궁금해서 찾아봤는데, 이러한 🔗글이 있더군요.
- Roll → Pitch → Yaw
(비행기의 수평을 잡고, 위로 향한 후, 방향 잡기)
3) 오일러 각으로 회전 행렬과 로컬 벡터 구하기
*Y-up 오른손 좌표계 기준입니다.
각 표준 기저 벡터의 로컬 축을 중심으로 회전한 양을 기입하는 방식이 오일러 각이라고 했지만, 결국 수많은 오브젝트들에 대해 빠르고 효율적인 계산을 하기 위해서는 행렬을 사용해야 합니다. 회전 행렬을 만들기 위해, 먼저 각 축에 대한 회전을 한 번 살펴보죠.
우선 \(X\) 축 회전부터 보겠습니다. 오른손 좌표계의 +방향 회전은 회전축을 기준으로 반시계방향이므로, \(X\) 를 방향으로 오른손 엄지를 뻗었을 때, 나머지 네 손가락이 감기는 방향이 +\(Y\) 축에서 +\(Z\) 축입니다.
이러한 내용을 토대로, \(X\) 축 회전에 대한 행렬은 다음과 같이 설계할 수 있습니다.
$$ R_x=\left[\begin{matrix}1&0&0\\0&cos\theta&-sin\theta\\0&sin\theta&cos\theta\\\end{matrix}\right] $$
\(Y\) 축도 똑같은 방식으로 만들 수 있습니다.
$$ R_y=\left[\begin{matrix}cos\theta&0&sin\theta\\0&1&0\\-sin\theta&0&cos\theta\\\end{matrix}\right]\\ $$
마지막으로 \(Z\) 축에 대한 회전은 2차원 공간에서 익숙하게 했던, 회전 변환입니다.
$$ R_z=\left[\begin{matrix}cos\theta&-sin\theta&0\\sin\theta&cos\theta&0\\0&0&1\\\end{matrix}\right] $$
이제 이 3개의 행렬을 통해 회전 행렬을 만들어 보겠습니다. Y-up 오른손 좌표계는 \(X\) 가 Pitch 회전축, \(Y\) 가 Yaw 회전축, \(Z\) 가 Roll 회전축이 됩니다. 그리고, 아까 위에서 수많은 엔진들이 Roll → Pitch → Yaw 순서로 적용한다고 했었죠?. Yaw, Pitch, Roll의 회전각을 각각 \(\alpha, \ \beta, \ \gamma\) 라고 하면, 다음과 같은 3차원 회전 행렬 \(R\) 이 완성됩니다!
$$ R_\alpha\cdot\ R_\beta\cdot\ R_\gamma=\left[\begin{matrix}cos\alpha&0&sin\alpha\\0&1&0\\-sin\alpha&0&cos\alpha\\\end{matrix}\right]\left[\begin{matrix}1&0&0\\0&cos\beta&-sin\beta\\0&sin\beta&cos\beta\\\end{matrix}\right]\left[\begin{matrix}cos\gamma&-sin\gamma&0\\sin\gamma&cos\gamma&0\\0&0&1\\\end{matrix}\right] $$
$$ \therefore R=\left[\begin{matrix}cos\alpha cos\gamma+sin\alpha sin\beta sin\gamma&-cos\alpha sin\gamma+sin\alpha sin\beta cos\gamma&sin\alpha cos\beta\\cos\beta sin\gamma&cos\beta cos\gamma&-sin\beta\\-sin\alpha cos\gamma+cos\alpha sin\beta sin\gamma&sin\alpha sin\gamma+cos\alpha sin\beta cos\gamma&cos\alpha cos\beta\\\end{matrix}\right] $$
이렇게 구한 회전 행렬은 3차원 공간의 회전 행렬과 동일하므로, 위 식으로부터 각각의 로컬 벡터를 얻을 수 있습니다.
- \(x_{local}=(cos\alpha\ cos\gamma+sin\alpha\ sin\beta\ sin\gamma, \ cos\beta\ sin\gamma, \ -sin\alpha\ cos\gamma+cos\alpha\ sin\beta\ sin\gamma) \)
- \( y_{local}=(-cos\alpha\ sin\gamma+sin\alpha\ sin\beta\ cos\gamma, \ cos\beta\ cos\gamma, \ sin\alpha\ sin\gamma+cos\alpha\ sin\beta\ cos\gamma) \)
- \( z_{local}=(sin\alpha\ cos\beta, \ -sin\beta, \ cos\alpha\ cos\beta) \)
3. 오일러 각의 문제점
1) 짐벌락(Gimbal-Lock) 현상
오일러 각은 3개의 축에 대해 각각 회전을 나누어 적용하기 때문에, 이전에 회전했던 결과에 따라 지금 회전한 결과 모습이 달라집니다. 즉, 각 축들은 서로 독립이 아닌 종속 관계에 있다는 소리죠. 종속 관계에 있기 때문에, 한 축을 회전해도 다른 축도 덩달아 회전하게 되는 것입니다.
그렇기에 각 축에 대해 회전을 진행하다 보면, 어느 순간 두 개 혹은 세 개의 축이 겹치게 되어 회전각이 소실되는 상황이 발생합니다. 이것을 짐벌락(Gimbal-Lock) 현상이라고 부릅니다.
이러한 짐벌락 현상은 언제 정확히 발생하는지 알아내기가 까다롭기 때문에, 이러한 문제를 예방하기 위해 방어 코드를 작성 하는 데에도 한계가 있습니다. 이에 따라, 회전을 안전하게 구현할 수가 없다는 큰 단점이 존재하게 되는 것이죠.
2) 회전의 보간 계산
회전하는 애니메이션이 있다고 한다면, 회전의 시작과 끝 키 프레임을 지정하고, 시간에 따라 서서히 보간하는 기능이 필요하게 됩니다. 이에 대해, 오일러 각은 한 축의 회전만 사용하는 경우에는 보간이 가능합니다. 다음과 같이 3개의 오일러 각 회전이 있다고 하면,
$$ R_1 = (0º, \ 15º, \ 0º) $$
$$ R_2 = (0º, \ 30º, \ 0º) $$
$$ R_3 = (0º, \ 45º, \ 0º) $$
그런데 Y축으로 "15도 회전 + 30도 회전"한 결과물이나, 처음부터 45도를 회전한 결과는 같습니다. 이를 통해, 직관적으로 다음의 식이 성립함을 알 수 있습니다.
$$ R_3 = R_2 \cdot R_1 $$
수식으로도 증명할 수 있는데, 회전 행렬의 각 요소를 풀어쓰면 다음과 같기 때문입니다. Yaw 회전만 진행되기 때문에, 나머지 Roll과 Pitch 회전은 항등 행렬 \(I\) 로 쓸 수 있습니다.
$$ R_{yaw3} \cdot I \cdot I = (R_{yaw2} \cdot I \cdot I) \cdot (R_{yaw1} \cdot I \cdot I) $$
$$ \rightarrow \ R_3 = R_2 \cdot R_1 $$
$$ \therefore \theta_3 = \theta_2 + \theta_1 $$
하지만 다음과 같이 두 축의 회전을 사용하면, 위의 식은 더 이상 사용할 수 없게 됩니다.
$$ R_1 = (0º, \ 15º, \ 15º) $$
$$ R_2 = (0º, \ 30º, \ 30º) $$
$$ R_3 = (0º, \ 45º, \ 45º) $$
$$ R_{yaw3}\cdot I\cdot R_{roll3}\ne R_{yaw2}\cdot I\cdot R_{roll2}\cdot R_{yaw1}\cdot I\cdot R_{roll1} $$
$$ \therefore R_3\ne R_2\cdot R_1 $$
위 식이 동일하려면 같은 차원의 각이 서로 이어져 있어야 합니다. 하지만 위 식은 Yaw와 Roll 회전이 서로 번갈아가며 배치되어 있어 서로 다른 축을 통해 회전하기에 동일하다고 보장할 수가 없습니다.
따라서, 두 축 이상의 회전으로 지정된 오일러 각으로는 보간하는 방법이 오히려 복잡해지기에, 이 때는 3차원 공간에서 축각 방식의 회전 방법을 사용해야 합니다. 축각 방식은 임의의 축을 지정하고, 해당 축을 통해 시작 회전과 끝 회전을 지정하는 방식입니다. 대표적으로 로드리게스 회전 방식과 사원수가 있습니다.
'Computer Sciences > Game Mathemathics' 카테고리의 다른 글
[게임 수학] #20 | 원근 투영(Perspective Projection) (2) | 2023.12.06 |
---|---|
[게임 수학] #19 | 벡터의 외적(Cross Product) (2) | 2023.12.05 |
[게임 수학] # 17 | 3차원 공간 (4) | 2023.11.30 |
[게임 수학] #16 | 뷰 공간(View Space) (2) | 2023.11.29 |
[게임 수학] #15 | 게임 엔진(Game Engine) (1) | 2023.11.28 |