본문 바로가기

Unity3D Tank/Quaternion Rotation

Quaternion Rotation in Unity

유니티에서 사원수(Quaternion)을 이용하여 오브젝트를 회전하는 예


사원수는 어떤 3차원 공간상의 한 점을 임의의 축으로 회전변환하기 위한 정보를 저장하고 있으며 정점의 회전변환을 위한 수단으로 사용된다. 그러므로 사원수는 임의의 축 정보와 그 축을 중심으로 회전할 각도를 포함하고 있으며 3차원 공간에서 강체를 회전변환하는데 사용된다.


회전변환에는 행렬(Matrix)도 사용될 수 있지만 사원수를 사용하면 계산량이 줄어들어 성능에 더 유리하게 된다.

공간상의 오브젝트의 회전을 표현할 때 오일러 회전변환(Euler Rotation)도 사용할 수 있지만 일정한 회전량을 벗어나면 Gimbal Lock 현상으로 인해 오작동하게 되므로 유니티 내부에서 모든 회전변환은 사원수를 사용하여 이루어진다.


회전변환이란 강체의 정점들을 지정한 축을 중심으로 지정된 각도만큼 회전했을 때 그 정점들이 새로운 좌표에 위치하도록 변환하는 것을 의미한다.

그러므로 사원수는 회전변환을 위한 수단이며 회전변환의 대상은 강체(게임 오브젝트)의 정점이 되며 정점은 3차원 벡터로 표현이 가능하다.


특정 정점을 사원수를 이용하여 회전변환한 결과는 다음과 같이 사원수와 벡터의 곱셈 결과가 산출하는 새로운 벡터로 표현된다

사원수(회전축, 회적각도) * 벡터(정점좌표) = 새로운 벡터(새로운 정점)


[유니티에서 사원수를 이용하여 특정 정점을 회전변환하는 예]

//변환 대상 정점

Vector3 p = new Vector3(0f, 0f, 1f);

print ("변환전 정점 좌표="+p.x + ", " +p.y + ", " + p.z);


//회전변환을 위한 각도와 축정보를 이용하여 사원수를 생성한다

Quaternion q = Quaternion.Euler (new Vector3 (90f, 0f, 0f));


//사원수를 이용하여 특정 정점을 회전변환한다

Vector3 p2 = q * Vector3.forward;


//회전변환량을 확인한다

float angle = Vector3.Angle (Vector3.forward, p2);

print ("각도차이=" + angle); // 90


// 원래 벡터는 (0,0,1)이지만 X축을 중심으로 90도 회전변환 후에는 (0,-1,0)이 된다

print ("변환후 정점 좌표="+p2.x + ", " +p2.y + ", " + p2.z);



transform.rotation 표현에서 rotation은 사원수이므로 회전변환을 위한 수단으로 유니티 내부에서 사용하고 있다는 것을 알 수 있다

개발자는 transform.rotation 변수에 사원수를 할당할 수 있고 기존 회전변환과 결합하기 위해서는 사원수끼리 곱셈을 할 수도 있다.



// 매번 오브젝트의 변환전 상태에 변환을 위한 사원수를 적용하므로 회전변환이 발생하지 않음(한번만 회전)

private float tm;

void TransformRot1() {

if(tm<3f) {

transform.rotation = Quaternion.Euler (new Vector3 (1f, 0f, 0f));

tm += Time.deltaTime;

}else {

transform.rotation = Quaternion.Euler (new Vector3 (-1f, 0f, 0f));

tm += Time.deltaTime;

if(tm>6f) tm = 0f;

}

}


// 기존 변환에 새로운 변환을 결함하여 변환이 자연스럽게 이어짐

void TransformRot2() {

if(tm<3f) { // 3초동안 앞으로, 3초동안 뒤로 회전변환이 이어짐

transform.rotation *= Quaternion.Euler (new Vector3 (1f, 0f, 0f));

tm += Time.deltaTime;

}else {

transform.rotation *= Quaternion.Euler (new Vector3 (-1f, 0f, 0f));

tm += Time.deltaTime;

if(tm>6f) tm = 0f;

}

}



[유니티의 회전변환은 내부적으로 모두 사원수를 이용하므로 아래의 4개 함수의 내용은 결국 동일한 의미이다]

using UnityEngine;

using System.Collections;


public class QuaternionDemo : MonoBehaviour {


// Use this for initialization

void Start () {

}

// Update is called once per frame

void Update () {

//QuaternionRot1 ();

//QuaternionRot2 ();

//QuaternionRot3 ();

QuaternionRot4 ();

}


void QuaternionRot1() {

transform.Rotate (new Vector3 (1f, 0f, 0f));

transform.Rotate (new Vector3 (0f, 1f, 0f));

transform.Rotate (new Vector3 (0f, 0f, 1f));

}


void QuaternionRot2() {

transform.Rotate (new Vector3 (1f, 1f, 1f));

}


void QuaternionRot3() {

Quaternion Qpitch = Quaternion.Euler (new Vector3 (1f, 0f, 0f));

Quaternion Qyaw = Quaternion.Euler( new Vector3(0f,1f,0f));

Quaternion Qroll = Quaternion.Euler (new Vector3 (0f, 0f, 1f));

Quaternion q = Qpitch * Qyaw * Qroll; //변환을 위한 사원수는 서로 곱해서 한개로 만들 수 있다

transform.rotation *= q;

}


void QuaternionRot4() {

Quaternion q = Quaternion.Euler (new Vector3 (1f, 1f, 1f));

transform.rotation *= q;

}

}