개발이야기

[유니티] Euler, Quaternion 오일러각 쿼터니언 총 정리

Kim_Jack 2020. 3. 31. 00:29
반응형

[Unity] Euler, Quaternion 오일러각(짐벌락) 쿼터니언에 대하여!

 

Unity Euler 각도는 x,y,z 3 축을 기준으로 회전시키는 우리가 흔히 알고있는 각도계를 의미한다.

Euler Angle

 

각도계를 사용하면 우리 모두 삽질할 필요없이 아름답게 개발을 있을텐데,

Queternion 같은게 생겨서 우리를 괴롭히는가,,,

 

이유는 Euler angle 짐벌락('Gimbal-lock')이라는 문제가 있기 때문에 모든 각도 변환을 표현하는데 한계가 있기 때문이다. 

그 한계점을 보완하여 생겨난 게 쿼터니언(Quaternion)이다. 

 

짐벌락이란 무엇인가.

우선 간단하게 설명하자면, 같은 방향으로 오브젝트의 회전 축이 겹치는 현상이다.

https://www.youtube.com/watch?v=zc8b2Jo7mno&feature=youtu.be&t=2m9s

 

사진으로 간단하게 설명하겠다.

 

우선 ! 

 

다음 사진과 같이 x, y ,z 축을 가진 오브젝트가 있다고 해보자. 

저 화살표가 가리키는 방향을 계속 유의하자.

 

이 오브젝트의 x(빨간축) 축을 90도 회전시켜보겠다.

 

x축(빨간축)으로 90도 회전시킨 모습이다. 

화살표를 보면 오른쪽 방향을 가리키고 있다. 

 

여기까지는 문제가 없다. 

 

자, 그다음 Y축(초록색)으로 90도 회전시켜보겠다.

Y축으로 90 회전시키니 다음과 같은 모습이 된다.

Z 축과 X 축이 한축으로 합쳐지면서 한축에 대한 계산이 불가능해집니다.

이러한 현상을 바로 '짐벌락(Gimbal-lock)' 이라고 한다. 

 

이러한 짐벌락 현상이 생기는 이유는 

오일러 앵글이 자체적으로 설정되어있는 순서로 해당 축들을 개별적으로 계산하기 때문이다.

Unity에서는 오일러 앵글이 X, Y, Z 순서로 계산된다.

세 개의 축을 동시에 계산하지 않고 각 축을 독립적으로 판단하기에 다음과 같이 어쩌다가 겹쳐버리는 현상이 발생하는 것이다. 

이렇게 축이 겹쳐버리면 한 축에 대해서는 계산이 불가능하기에, 정확한 각도 계산이 불가능하다.

 

Unity로 2D 게임을 제작할 때는 오일러 각으로도 각도 구현에 문제가 없지만,

모든 각도를 통제해줘야 하는 3D 게임 같은 경우에는 오일러 앵글만으로는 구현에 한계가 있다. 

 

오일러 각의 이러한 문제를 해결하기 위해 나온 것이 바로 쿼터니언(Quaternion)이다. 

 

쿼터니언(Quaternion)

쿼터니언은 각 축을 한꺼번에 계산하기 때문에 짐벌락 문제가 발생하지 않는다.

 

Euler angle 과는 다르게 쿼터니언은 4개의 성분(x, y, z, w)으로 이루어져 있다. 

해당 성분은 벡터(x, y, z)와 스칼라(w)를 의미한다.

 

쿼터니언은 내부가 수학적으로 복잡하게 구현되어있어 이를 제대로 이해하지 못한다면 자유자재로 다루기는 상당히 까다롭다.

 

쿼터니언은 방향(orientation) (rotation) 둘을  표현할  있다.

하지만 쿼터니언의 회전은  orientation 에서 다른 orientation 으로 측정하기에 180 보다  값을 표현할  없다는 단점이 있다. 

 점이 쿼터니언을 직관적으로 이해할  없는  이유  하나이다.

 

다행히 유니티에는 이러한 쿼터니언을 간단하게 사용 가능하도록 만들어진 함수들이 다양하게 존재한다.

 

유니티 공식 문서에도 쿼터니언은 쓰기 어려우니 자기들이 만든 함수를 사용하도록 권장한다.

 

쿼터니언을 다루기 위해 사용되는 대표적인 함수들의 사용법을 정리해봤다. 

 

Quaternion.Euler

public static Quaternion Euler(float x, float y, float z);

유니티에서는 Quaternion.Euler 함수를 통해서 오일러각을 쿼터니언으로 변경시켜 사용한다.

해당 함수 인자에 오일러각을 넣으면 쿼터니언으로 변환된 값을 반환시켜준다. 

 

Euler 함수 사용법

transform.roation = Quaternion.Euler(new Vector3(120,60,100)); 

 

float x; 
void Update () 
{ 
	x += Time.deltaTime * 10; 
	transform.rotation = Quaternion.Euler(x,0,0); 
}

 

 

Quaternion.LookRotation

public static Quaternion LookRotation(Vector3 forwardVector3 upwards = Vector3.up);

번째 인자인 upwards upwards는 Vector3.up 으로 디폴트 인자가 세팅되어 있다.

번째 인자에  방향벡터를 입력하면 해당 자기 위치기준에서의 해당 방향벡터를 바라보게 된다.

 

어떠한 타겟을 향해 회전시키고 싶다면 다음과 같이 사용하면 된다.

Vector3 direction = (traget.position - this.transform.position).normalized;
Quaternion lookRotation = Quaternion.LookRotation(direction);
this.transform.rotation = lookRotation;

 

 

Quaternion.Slerp

public static Quaternion Slerp(Quaternion aQuaternion b, float t);

/from/ /to/사이를 /t/구형보간 합니다.

 

Quaternion.Slerp 함수는 쿼터니언의 중간값을 리턴 시켜준다.

Slerp 함수를 사용할 종종 번째 인자인 t 값에 대하여 헷갈리는데,

Slerp 함수는 lerp 함수에서 쓰이는 선형보간법이 아닌 구면선형보간법 기반으로 되있다는 점을 명심해야한다.

Lerp 함수와 Slerp 함수는 번째 인자에 따른 반환값이 다르다.

Slerp를사용하고 싶다면 선형보간법과 구면선형보간법의 차이를 숙지해야 원하는 각도 변환을 연출할 있을 것이다.

Lerp, Slerp 차이.

Lerp와 Slerp의 차이를 간단하게 그림으로 나타내면 다음과 같다. 

초록점에서 빨간 점으로 이동할 때 Lerp는 직선으로 Slerp는 곡선으로 보간 된다.

transform.rotation = Quaternion.Slerp(A.transform.rotation, B.transform.rotation, Time.deltaTime);

 

 

Quaternion.FromToRotation

public static Quaternion FromToRotation(Vector3 fromDirectionVector3 toDirection);

/fromDirection/에서 /toDirection/으로 회전한 rotation을 생성합니다.

 

FromToRotaion 함수는 fromDirection 의 방향벡터를 toDirection 으로 회전한 쿼터니언을 반환한다.

rotation(0,0,0)

rotation 값이 (0,0,0)인인 큐브이다.

transform.rotation = Quaternion.FromToRotation(Vector3.up, Vector3.right);

다음 함수를 적용 시키면

큐브의 up vector(초록색 화살표) 옆으로 꺽여있다.

카메라 위치 때문에 반대로 보이지만 큐브가 오른쪽으로 90 회전한 모습이다.

 

FromToRotation 함수는 중심축을 첫 번째 인자로 넣고 회전하고 싶은 방향벡터를 두 번째 인자로 넣어서 사용하면 될듯싶다.

반응형