KoreanFoodie's Study

[OpenGL ES] 11강 : 오일러 변환(Euler Transform) 과 쿼터니언(Quaternion) 본문

Game Dev/OpenGL ES

[OpenGL ES] 11강 : 오일러 변환(Euler Transform) 과 쿼터니언(Quaternion)

GoldGiver 2023. 4. 24. 02:02

이 강의는 유투브에 무료로 공개되어 있는 한정현 교수님의 컴퓨터 그래픽스 강좌를 정리한 글입니다. 자세한 내용은 강의를 직접 들으시거나 을 구입하셔서 확인해 보세요. 강의 자료는 깃헙 링크에 올라와 있습니다.

요약 :

1. 오일러 변환은 주 축(Principal Axes)을 기준으로 일정 각도의 회전을 연속적으로 적용할 때 사용할 수 있다. 그런데 오일러 변환의 결과값은 교환법칙이 성립하지 않는다.
 
2. 키프레임 보간을 할 때, 오일러 변환은 어색한 결과를 보여줄 수 있는데, 이를 해결하기 위해 쿼터니언을 사용한다. 쿼터니언은 사원수를 이용한다.
 
3. 쿼터니언을 이용하면 임의의 축에 대해 𝛉 만큼의 각도로 점/벡터를 회전시키는 회전 행렬을 쉽게 만들어 낼 수 있다(qpq*). 그리고 쿼터니언 사이의 보간도 가능하다!

오일러 변환(Euler Transform)

x, y, z 축으로 임의의 각도만큼 회전을 하다보면, 물체는 임의의 방향을 바라보게 된다. 이런 방식으로 오브젝트의 방향을 결정하는 것을 오일러 변환(Euler transform)이라고 한다. 그리고 이 때의 회전 각도 (θ1, θ2, θ3) 를 오일러 각(Euler angles)이라고 한다.

위에서 z, y, x 좌표로 회전을 한 행렬의 결과값은 결국 하나의 행렬로 표현할 수 있다.

 

그런데 오일러 변환은 어떤 축의 회전을 먼저 적용했느냐에 따라 그 결과값이 다르게 나올 수 있다. 위의 회전은 y, x, z 순으로, 아래 회전은 x, y, z 순으로 회전을 적용했다.

 

애니메이션을 만들 때는, 키 프레임(keyframe) 을 시니어가 먼저 그리고(예를 들어 1초에 24장이라면 1, 13, 24 번째 장), 주니어 아티스트가 해당 키 프레임의 중간 단계 프레임(in-between frame)을 그릴것이다(예를들어 2~12, 14~23 장).

30-fps 의 컴퓨터 애니메이션에선 1초당 30 프레임보다 훨씬 작은 프레임이 정의되어 있을 것이다. 실시간 컴퓨터 애니메이션에선, 이러한 in-between frame 을 컴퓨터가 자동으로 채워줄 것이다.

중요한 정보(위치나 회전)은 키 프레임에 정의되어 있고, in-between frame 은 보간을 통해 생성될 것이다.

위의 그림을 예시로 보면, 키프레임 0 과 1 사이에 도형들이 보간되는 것을 볼 수 있다.

 

위의 주전자를 보자. 키프레임 0 에서 2 까지 가는 과정에서 샘플링된(보간된) 주전자 그림들이 그려지고 있다. 위 방식은 방향과 위치에 대해 선형 보간한 결과값들을 보여준다.

 

그런데 사실 완전한 선형 보간은 보간이 되는 지점에서 어색한 동작을 보여줄 수 있다. 이를 완화하기 위해서는, 위의 오른쪽 그래프처럼 약간의 smoothness 를 줄 수 있다.

 

그런데 오일러 각은 항상 정확하게 보간이 되지는 않으므로, 키프레임 애니메이션에 사용하기에는 적합하지 않다.

위의 그림을 보자. 키프레임 0과 키프레임 1에서의 두 점이 첫째 줄 과 둘째 줄에 잘 나와 있다.

그런데 두 점의 중간값이 보간이 된다고 하면... 세번째 줄처럼 결과가 나오는데, 문제가 하나 있다.

키프레임 0 과 1 에서의 두 점은 모두 y-z 평면에 놓여 있다. 그런데, 이 둘을 보간한 결과는... y-z 평면에 있지 않다!!

그렇다면 이 문제를 어떻게 해결해야 할까?

 

 

쿼터니언(Quaternion)

그 대안이 바로 쿼터니언이다. 쿼터니언은 복소수를 확장한 녀석이다. 이때 qw 는 실수부, 나머지는 허수부이다. 이를 사원수라고 부르기도 한다.

위에서 p 와 q 를 곱하면, pq 의 결과값을 아래와 같이 표현할 수 있다 :

또한, 사원수를 Conjugate (켤레) 복소수를 취해서 표현할 수도 있는데... pq 에 대해 켤레 복소수를 취하면 q*p* 가 된다.

 

3차원으로 넘어가기 전에, 쿼터니언을 이용해 2D 공간에서의 회전을 살펴보자.

위에서 p 좌표를 p' 좌표로 𝛉 만큼 회전시킨다고 하자. 이때, (x', y') 를 다음과 같이 표현할 수 있는데...

(x, y) 를 x + yi 라고 놓고, 이를 p 라고 정의한 다음, 𝛉 각만큼 회전시키는 unit 사원수를 cos𝛉 + sin𝛉i 라고 정의한 후 q 라고 하자.

이때, 우리는 pq 의 값을 아래와 같은 식으로 표현할 수 있는데...

놀랍게도, 실수부와 허수부가 각각 x' , y' 와 일치하게 된다!

 

이제 3차원 회전으로 넘어오자. 3차원 회전에서는 2차원과 달리 회전축이 필요하다.

점 p 가 (x, y, z), p' 가 (x', y', z') 라고 하자. 먼저, p 를 이용해 쿼터니언 p 를 정의하겠다.

그리고, unit vector 인 u (회전축의 unit vector) 를 이용해 쿼터니언 q 를 아래와 같이 정의한다(단, q 는 unit quaternion 이어야 한다. 뭐, 생각해보면 당연한 일이다) :

그럼 qpq* 의 허수부가 회전된 벡터의 결과값이 된다!

 

이제 우리는 임의의 축에 대해 회전을 적용할 수 있게 되었다! (임의의 축에 대해 normalize 를 진행한 unit vector 를 구하기만 하면 되니까)

 

그런데 쿼터니언도 보간이 된다! 아래와 같이 말이다... 😅 (이때, 점 p 에서는 t 가 0, 점 q 에서는 t 가 1 일 것이다)

 쿼터니언 pq 를 곱하면, 내적과 같은 결과가 나오는데... 아크 코사인을 취하면 Φ 값을 구할 수 있다. 아, 그때의 Φ 값은 물론... t 에 따라 달라질 것이다. 예를 들어, t 가 0.5 일 경우에는, 0.5 * Φ 값을 위의 보간식에 넣으면 된다 😉

 

그럼 4차원으로 표현된 사원수를 행렬로 바꾸면 어떻게 될까? 바로 위의 사진이 그것을 잘 보여주고 있다. 증명은 책을 참고하자 😂

 

심지어, 회전 행렬을 통해 쿼터니언을 뽑아낼 수도 있다. 위 부분은 참고로만 기억해 두자 😮

 

요약을 하면, 임의의 3D 공간에서의 회전은 쿼터니언과 오일러 변환으로 표현될 수 있다. 쿼터니언은 원에서의 선형 보간으로 표현될 수 있으며, 회전 행렬로 변환될 수 있다.

쿼터니언이 키프레임마다 정의되어 있으면, 그 보간된 프레임을 qpq* 식을 통해 구할 수 있다! 오일러 각이 정의되어 있으면, 오일러 각을 이용해 쿼터니언으로 변환만 해주면, 같은 방식으로 보간된 프레임을 구할 수 있다 😀

Comments