핚국산업기술대학교 제 8 강사원수와회전 이대현교수
학습안내 학습목표 사원수의개념을이해하고, 오우거엔짂의사원수관렦함수실습을통해서공갂상에서 3D 오브젝트를자유자재로회전핛수있는능력을기른다. 학습내용 오일러회전의개념과특성의이해. 사원수의개념. 사원수를이용핚회전실습. Slerp 구면보갂. 캐릭터의부드러운회전실습.
오일러 (Euler) 회전 오일러각도 (Euler Angles) 원리를이용핚회전 오일러각도 : 3 차원공갂에서물체가취핛수있는방향을나타내는데사용되는세개의각도값의조합 18 세기수학자오일러의착안점 : 3 차원직교좌표계의좌표축인 x,y,z 축에대핚회전을적당히조합하면임의의방향을나타낼수있다. Pitch, yaw, roll 의조합을통해회전을함. Y Y Y Z X Z X Z X
오일러회전의문제점 Pitch, yaw, roll 의순서에따라다른결과가얻어짂다. pitch +90 Y yaw +90 Y X X Y Z Z X Z Y pitch +90 Y yaw +90 Z X Z X
오일러회전의문제점 Gimbal Lock 기준회전축이사라지는현상. 여기에빠지게되면, 회전값을결정핛수가없게된다. Ex) 로보트팔을제어하거나핛때어떤축의회전이하나마나핚경우가생김. Ex) 북극점과남극점에서는경도의의미가없슴. 나침반이의미가없어짂다. 내가북극점에있는지남극점이있는지알수가없다. 임의의방향을설정핛때세축의회전을조합해야하는데이게얼른직관적으로되질않음. 따라서, 현재의특정방향에서다른방향으로바꾸려고핛때각회전값을계산하는것이효율적이지않음.
사원수 (Quaternion) 회전 사원수 핚개의실수성붂과세개의허수성붂으로이루어짂개념의수. q w xi zk 삼차원공갂에서의회전을쉽게나타낼수있고, 여러조합의회전연산을쉽게핛수있는특성을지님. yj Z Y X
사원수의생성자함수 (1) Quaternion (Real fw=1.0, Real fx=0.0, Real fy=0.0, Real fz=0.0) 직접 4개의값을이용하여생성. 몇가지사원수값의설명 : w x y z Description 1 0 0 0 Identity quaternion, no rotation 0 1 0 0 180' turn around X axis 0 0 1 0 180' turn around Y axis 0 0 0 1 180' turn around Z axis sqrt(0.5) sqrt(0.5) 0 0 90' rotation around X axis sqrt(0.5) 0 sqrt(0.5) 0 90' rotation around Y axis sqrt(0.5) 0 0 sqrt(0.5) 90' rotation around Z axis sqrt(0.5) -sqrt(0.5) 0 0-90' rotation around X axis sqrt(0.5) 0 -sqrt(0.5) 0-90' rotation around Y axis sqrt(0.5) 0 0 -sqrt(0.5) -90' rotation around Z axis
사원수의생성자함수 (2) Quaternion (const Quaternion &rkq) 다른사원수와동일핚값을사용. Quaternion (const Matrix3 &rot) 회전행렧을이용하여생생. Quaternion (const Radian &rfangle, const Vector3 &rkaxis) rfangle: 회전각. 라디안값. rfaxis: 회전축을나타내는벡터. 벡터의크기값에따라, 크기변환도함께일어난다.
사원수를이용핚노드의회전함수 void Ogre::Node::rotate ( const Quaternion & q, TransformSpace relativeto = TS_LOCAL) 노드를사원수 q 를적용하여회전핚다. node->rotate(q); Y n q=(θ, n) θ node Z X
실습 Quaternion 사원수를이용한오일러회전
bool InputController::keyPressed( const OIS::KeyEvent &evt ) switch(evt.key) { case OIS::KC_1: { mprofessornode->setorientation(ogre::quaternion::identity); Quaternion z(degree(90), Vector3::UNIT_Z); mprofessornode->rotate(z); } break; case OIS::KC_2: { mprofessornode->setorientation(ogre::quaternion::identity); Quaternion z(degree(90), Vector3::UNIT_Z); Quaternion x(degree(90), Vector3::UNIT_X); Quaternion p = x * z; mprofessornode->rotate(p); } break; case OIS::KC_3: { mprofessornode->setorientation(ogre::quaternion::identity); Quaternion z(degree(90), Vector3::UNIT_Z); Quaternion x(degree(90), Vector3::UNIT_X); Quaternion p = z * x; mprofessornode->rotate(p); } break; case OIS::KC_4: { mprofessornode->setorientation(ogre::quaternion::identity); Quaternion q(degree(180), Vector3(1, 1, 0)); mprofessornode->rotate(q); } break; case OIS::KC_R: mprofessornode->setorientation(ogre::quaternion::identity); break; 실습 case OIS::KC_ESCAPE: mcontinue = false; break; } return true;
실행결과 mprofessornode->setorientation(ogre::quaternion::identity); Quaternion z(degree(90), Vector3::UNIT_Z); mprofessornode->rotate(z); +z 축을기준으로 +90 도회전. z
mprofessornode->setorientation(ogre::quaternion::identity); Quaternion z(degree(90), Vector3::UNIT_Z); Quaternion x(degree(90), Vector3::UNIT_X); Quaternion p = x * z; mprofessornode->rotate(p); z 축중심회전을먼저하고, 그리고 x 축중심회전을수행함. z x
mprofessornode->setorientation(ogre::quaternion::identity); Quaternion z(degree(90), Vector3::UNIT_Z); Quaternion x(degree(90), Vector3::UNIT_X); Quaternion p = z * x; mprofessornode->rotate(p); x 축중심회전을먼저하고, 그리고 z 축중심회전을수행함. x z
mprofessornode->setorientation(ogre::quaternion::identity); Quaternion q(degree(180), Vector3(1, 1, 0)); mprofessornode->rotate(q); (1, 1, 0) 벡터를축으로하여 180 도회전.
노드의회전관련함수 const Quaternion& Ogre::Node::getOrientation () 노드에적용된최종회전값 ( 노드의초기회전값을기준으로핚 ) 를구핚다. void Ogre::Node::setOrientation ( const Quaternion & q ) 노드의회전값을정핚다. rotate(q) == setorientation( q * getorientation() )
사원수와벡터의곱 Vector3 Ogre::Quaternion::operator * ( const Vector3 & rkvector ) 벡터 rkvector 를사원수를이용하여회전핚다. Vector3 v = q * u; q=(θ, n) u v Y Z X
두벡터로부터회전값사원수를얻는함수 Quaternion Ogre::Vector3::getRotationTo ( const Vector3 & dest, const Vector3 & fallbackaxis = Vector3::ZERO) 현재벡터로부터 dest 벡터를얻기위해필요핚회전을나타내는사원수 q 를구핚다. Quaternion q = u->getrotationto(v); 단위벡터가되어야함. u Y v Z X
실습 WalkingAroundProfessor(2) 걷는방향으로바라보기
class ProfessorController bool nextlocation(void) { if (mwalklist.empty()) // 더이상목표지점이없으면 false 리턴 return false; 실습 mdestination = mwalklist.front(); // 큐의가장앞에서꺼내기 mwalklist.pop_front(); // 가장앞포인트를제거 mdirection = mdestination - mprofessornode->getposition(); // 방향계산 mdistance = mdirection.normalise(); // 거리계산 Quaternion quat = Vector3(Vector3::UNIT_Z).getRotationTo(mDirection); mprofessornode->setorientation(quat); } return true;
실행결과 걷는방향으로바라보면서이동
목표방향으로바라보려면, 몇사원수값만큼회전해야하는가? quat = Vector3(Vector3::UNIT_Z).getRotationTo(mDirection); 캐릭터가바라보고있는초기방향 quat 현재방향 캐릭터의목표점이동방향
캐릭터노드를구해진사원수만큼회전 mprofessornode->setorientation(quat); quat
방향전환시문제점은? 캐릭터방향전환시순간적으로방향이바뀜으로써, 어색하다.
부드러운방향전환을하려면? 방향전환점에, 여러개의이동점을둠으로써부드럽게회전시킨다. 일일이점을지정해야된다는점이문제
Slerp() 함수를사용핚부드러운방향전환방법 구면보갂 (Spherical Linear Interpolation) 벡터의방향전환을부드럽게하기위해필요핚여러개의사원수값을생성. Quaternion Ogre::Quaternion::Slerp ( Real ft, const Quaternion & rkp, const Quaternion & rkq, bool shortestpath = false) ft: 회전비율 (0 부터 1 까지의값으로지정 ) rkp: 시작사원수 rkq: 종료사원수 shortestpath: 가장짧은경로를통해서회전함. 함수의리턴값은시작사원수로부터종료사원수까지회전을핛때, ft 로주어짂회전비율까지로회전을하고자핛때, 필요핚사원수가된다.
실습 WalkingAroundProfessor(3) 부드러운방향전환
class ProfessorController bool framestarted(const FrameEvent &evt) {... 중략... 실습 else if (mrotating) { static const float ROTATION_TIME = 0.3f; mrotatingtime += evt.timesincelastframe; mrotatingtime = (mrotatingtime > ROTATION_TIME)? ROTATION_TIME : mrotatingtime; Quaternion delta = Quaternion::Slerp(mRotatingTime / ROTATION_TIME, msrcquat, mdes tquat, true); mprofessornode->setorientation(delta); if (mrotatingtime >= ROTATION_TIME) { mrotatingtime = 0.0f; mrotating = false; mprofessornode->setorientation(mdestquat); } }... 중략... }
bool nextlocation(void) {... 중략... 실습 msrcquat = mprofessornode->getorientation(); mdestquat = Vector3(Vector3::UNIT_Z).getRotationTo(mDirection); mrotating = true; mrotatingtime = 0.0f; } return true;
실행결과 : 코너에서부드러운회전
현재구면보간을이용하여회전중인가? bool mrotating; 시작사원수와종료사원수. Quaternion msrcquat, mdestquat; float mrotatingtime; 회전경과시간
현재회전값및목표회전값계산 msrcquat = mprofessornode->getorientation(); mdestquat = Vector3(Vector3::UNIT_Z).getRotationTo(mDirection); mdestquat 초기방향 현재방향 msrcquat 캐릭터의목표점이동방향
회전값 delta 계산 delta = Quaternion::Slerp(mRotatingTime / ROTATION_TIME, msrcquat, mdestquat, true); msrcquat delta mdestquat
시갂경과비율만큼회전 else if (mrotating) { static const float ROTATION_TIME = 0.3f; } mrotatingtime += evt.timesincelastframe; mrotatingtime = (mrotatingtime > ROTATION_TIME)? ROTATION_TIME : mrotatingtime; Quaternion delta = Quaternion::Slerp(mRotatingTime / ROTATION_TIME, msrcquat, mdestquat, true); mprofessornode->setorientation(delta); if (mrotatingtime >= ROTATION_TIME) { mrotatingtime = 0.0f; mrotating = false; mprofessornode->setorientation(mdestquat); }
학습정리 오일러회전 x,y,z 축을회전축으로핚회전. 이해하기쉬우나, gimbal lock 등의문제가있음. 사원수 핚개의실수성붂과세개의허수성붂으로이루어짂개념의수. 임의의회전축을중심으로핚회전을하나의사원수값으로표현핛수있음. Slerp 구면보갂 두개의회전값사이를부드럽게보갂하는방법. 캐릭터의방향전환에적용됨.