【Ogre-windows】旋转矩阵及位置解析
前言
這篇博客主要針對三種問題
- 如何創(chuàng)建動畫幀
- 如何獲取全局位置
- 如何計算全局旋轉矩陣
仿真環(huán)境為VS2013+Ogre1.10.9與matlab驗證
創(chuàng)建動畫幀
這里只做一個簡單的實驗: 將自帶的人物模型Jaiqua的run運動給新創(chuàng)建的運動myrun中并播放,直接貼代碼了
void JaiQua::createanim(){SkeletonInstance* skel = mEntity->getSkeleton();Animation *anim = skel->createAnimation("mywalk", 1.2667);anim->setInterpolationMode(Ogre::Animation::IM_SPLINE);Animation *orianim = skel->getAnimation("Walk");//原始運動Animation::NodeTrackIterator tracks = orianim->getNodeTrackIterator();while (tracks.hasMoreElements()){NodeAnimationTrack *track = tracks.getNext();//原始運動各關節(jié)遍歷Bone *bone = skel->getBone(track->getHandle());//獲取原始運動的各關節(jié)Bone *nbone = skel->getBone(bone->getName());//nbone->setManuallyControlled(true);Real framenum = track->getNumKeyFrames();//原始各關節(jié)總幀數(shù)NodeAnimationTrack *tracksnew = anim->createNodeTrack(nbone->getHandle(),nbone);//為新運動創(chuàng)建一個關節(jié)//依據(jù)原始運動各關節(jié)每幀數(shù)據(jù)對新運動各關節(jié)賦值cout << bone->getName() << ": ";for (Real i = 0; i < framenum; i++){Real currentframe = track->getKeyFrame(i)->getTime();//cout << "當前幀" <<currentframe << " "<<endl;TransformKeyFrame *newKf = tracksnew->createNodeKeyFrame(currentframe);//為當前關節(jié)創(chuàng)建一幀,輸入參數(shù)是時間點TransformKeyFrame *oriKf = track->getNodeKeyFrame(i);//獲取當前幀的原始運動關節(jié),輸入參數(shù)是時間的索引//關節(jié)旋轉賦值Quaternion quat = oriKf->getRotation();Ogre::Matrix3 mat;quat.ToRotationMatrix(mat);newKf->setRotation(Quaternion(mat));}cout << endl;}mEntity->refreshAvailableAnimationState(); }流程:
- 這里需要注意的是Jaiqua的初始關節(jié)數(shù)是大于當前幀具有運動數(shù)據(jù)的關節(jié)數(shù)的,所以賦值的時候需要先看看初始的Walk運動中到底都給哪些關節(jié)賦值了, 方法就是代碼中的迭代器Animation::NodeTrackIterator tracks = orianim->getNodeTrackIterator(); 然后不斷循環(huán)即可, 獲取下一個關節(jié)的位置是迭代器的next函數(shù).
- 在循環(huán)體內部就可以依據(jù)每幀對每個關節(jié)的旋轉矩陣賦值, 關鍵一步是針對新運動mywalk創(chuàng)建關鍵幀, 函數(shù)是createNodeKeyFrame(),傳入參數(shù)是當前關鍵幀的時間點,比如0.3333,而非1、2之類的
- 創(chuàng)建了關鍵幀, 就可以獲取一下原始運動Walk的當前關節(jié)當前幀getNodeKeyFrame(),注意傳入參數(shù)是時間點的索引如1、2,指示的是第幾幀, 而非0.333之類的
- 最后就可以獲取此關節(jié)當前幀的選轉四元數(shù)getRotation, 隨后setRotation給新的運動的當前幀當前關節(jié)即可
播放動畫的時候直接獲取新運動即可
//創(chuàng)建RobotmEntity = mSceneMgr->createEntity("jaiqua.mesh");mNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(Ogre::Vector3(0, 0, 25.0));mNode->attachObject(mEntity);//創(chuàng)建動畫createanim();//動畫狀態(tài)mAnimationState = mEntity->getAnimationState("mywalk");mAnimationState->setEnabled(true);mAnimationState->setLoop(true);骨骼旋轉矩陣、幀旋轉矩陣
旋轉一般都包含局部旋轉和全局旋轉, 需要注意的是與ASF/AMC動捕數(shù)據(jù)類似, 除了關節(jié)長度信息外, 骨骼自身每個還有旋轉信息, 這與BVH動畫數(shù)據(jù)不同, BVH骨骼只有offset骨骼長度信息, 這就導致了當前幀的局部旋轉并非由當前動畫幀中的rotation angle-axis直接得到, 還必須考慮骨骼自身的旋轉, 即當前幀當前關節(jié)局部旋轉是骨骼定義的旋轉與幀動畫定義的旋轉的乘積關系,具體是誰乘以誰,下面驗證
在渲染每一幀的時候,也就是函數(shù)frameRenderingQueued中, 我們加入如下語句獲取某個關節(jié)的骨骼內部旋轉(原始局部)、當前局部旋轉
Ogre::SkeletonInstance *skel = mEntity->getSkeleton();Ogre::Animation *anim = skel->getAnimation("mywalk");Animation::NodeTrackIterator tracks = anim->getNodeTrackIterator();while (tracks.hasMoreElements()){NodeAnimationTrack *track = tracks.getNext();TransformKeyFrame *kf = track->getNodeKeyFrame(mAnimationState->getTimePosition()); //是索引,而非時間 Bone *bone = skel->getBone(track->getHandle());if (bone->getName() == "Spineroot"){cout << "時間: " << mAnimationState->getTimePosition() << " 關節(jié): " << bone->getName() << endl;Ogre::Quaternion init_localrot = bone->getInitialOrientation();Ogre::Quaternion localrot = bone->getOrientation(); //bone->convertWorldToLocalOrientation(bone->getOrientation());Ogre::Matrix3 initlocalmat,localmat;localrot.ToRotationMatrix(localmat);init_localrot.ToRotationMatrix(initlocalmat);cout << "原始局部" << endl;showmatrix(initlocalmat);cout << "當前局部" << endl;showmatrix(localmat);cout << "----------------------------" << endl;}}提取出Spineroot關節(jié)的第0時刻,即第一幀的旋轉信息
那么,這個數(shù)據(jù)怎么來的呢?用matlab仿真, 流程是就是讀取Jaiqua骨骼和運動幀的軸角對, 并用vrrotvec2mat()轉換成旋轉矩陣, 注意這個函數(shù)接受的是一個數(shù)組, 數(shù)組前三個元素分別為(x,y,z)角度, 第四個元素為角度制的角度(不要用deg2rad轉弧度制),但是轉換的結果通常與Ogre的轉換函數(shù)有點差異, 實驗一下
可以發(fā)現(xiàn)它倆剛好互為轉置, 所以在matlab中仿真的時候注意一下這一點,回過頭看我們的問題:原始局部和當前局部是如何通過Jaiqua.skeleton中的數(shù)據(jù)計算得到?
原始局部很簡單,其實就是bones中定義的初始骨骼旋轉
當前局部也即第1幀Spineroot旋轉是如何得到的?
最終的驗證結果是
當前關節(jié)局部旋轉矩陣=動畫幀定義旋轉×初始骨骼旋轉當前關節(jié)局部旋轉矩陣=動畫幀定義旋轉\times 初始骨骼旋轉 當前關節(jié)局部旋轉矩陣=動畫幀定義旋轉×初始骨骼旋轉
#關節(jié)位置
全局位置關于這一個, 我目前還沒搞清楚每幀keyframe中translate參與計算的方式, 按理說這個數(shù)據(jù)是沒有任何作用的, 因為我們知道初始骨骼, 直接依據(jù)當前幀定義的旋轉矩陣就可以得到當前姿態(tài), 無需額外的translate設置, 而且在matlab的仿真結果證明此數(shù)據(jù)并無用
【更新日志】并非無用, 這個對整個人體的位置還是有用的, 而除了指定人體位置的關節(jié)之外的關節(jié), 這個translate是幾乎無用的, 而且可以發(fā)現(xiàn)它們的值都很小很小, 基本都乘以e?16e^{-16}e?16了
在matlab中求解關節(jié)位置的關鍵代碼如下
tdof = squeeze(localRotM(ind,:,:)); xyzStruct(ind).xyz=skel.tree(ind).offset*xyzStruct(parent).rot+xyzStruct(parent).xyz; xyzStruct(ind).rot=tdof*skel.tree(ind).axisOrder*xyzStruct(parent).rot;tdof代表的就是當前幀當前關節(jié)的局部旋轉, skel.tree(ind).offset代表初始骨骼定義的骨骼長度即position, skel.tree(ind).axisOrder代表的就是初始骨骼定義的旋轉, xyzStruct(parent).rot代表當前關節(jié)父關節(jié)的全局旋轉,最終計算的位置坐標如下:
Jaiqua0 0 0GlobalSRT1.0e-15 *-0.1110 0 0Spineroot2.1796 8.7556 -53.7141Spine012.1796 8.7556 -53.7141Spine022.2534 10.6250 -53.7025Spine032.3300 12.5657 -53.6904Lshoulderroot2.3426 14.3376 -53.6515Lshoulder2.3426 14.3376 -53.6515Larmroot0.7550 14.1863 -53.5923Lbicept0.7550 14.1863 -53.5923Lforearm-0.6631 11.6248 -53.2319Lhandroot-1.8638 9.4263 -53.9285Lhand-1.8638 9.4263 -53.9285Lfingers-2.5909 8.5843 -54.4267Rshoulderroot2.3382 14.3351 -53.6398Rshoulder2.3382 14.3351 -53.6398Rarmroot3.9311 14.3725 -53.7863Rbicept3.9311 14.3725 -53.7863Rforearm5.0354 11.6399 -53.6601Rhandroot4.6609 9.0743 -53.8534Rhand4.6609 9.0743 -53.8534Rfingers4.7810 7.8851 -54.0929neckroot2.3827 14.9406 -53.5743neck2.3827 14.9406 -53.5743head2.3325 15.4175 -53.7158Llegroot0.9231 8.9329 -53.5043Lthigh0.9231 8.9329 -53.5043Lshin1.1398 4.0498 -54.2153Lfooteffector1.5438 -0.4803 -53.4399Lfoot1.5438 -0.4803 -53.4399Ltoe1.2300 -1.0743 -54.5649Rlegroot3.3706 8.5802 -53.9321Rthigh3.3706 8.5802 -53.9321Rshin2.7365 3.8570 -55.2306Rfootroot3.2952 1.1392 -51.5247Rfoot3.2952 1.1392 -51.5247Rtoe3.0920 -0.0266 -52.0873在Ogre中獲取當前關節(jié)相對于根關節(jié)的位置是bone->_getDerivedPosition()函數(shù), 即在渲染函數(shù)中添加如下代碼
Ogre::SkeletonInstance *skel = mEntity->getSkeleton();Ogre::Animation *anim = skel->getAnimation("mywalk");Animation::NodeTrackIterator tracks = anim->getNodeTrackIterator();while (tracks.hasMoreElements()){NodeAnimationTrack *track = tracks.getNext();TransformKeyFrame *kf = track->getNodeKeyFrame(mAnimationState->getTimePosition()); //是索引,而非時間 Bone *bone = skel->getBone(track->getHandle());if (mAnimationState->getTimePosition()==0){//bone->getName() == "Spineroot"cout << "時間: " << mAnimationState->getTimePosition() << " 關節(jié): " << bone->getName() << endl;Ogre::Quaternion init_localrot = bone->getInitialOrientation();Ogre::Quaternion localrot = bone->getOrientation(); //bone->convertWorldToLocalOrientation(bone->getOrientation());Ogre::Matrix3 initlocalmat,localmat;localrot.ToRotationMatrix(localmat);init_localrot.ToRotationMatrix(initlocalmat);//cout << "原始局部" << endl;//showmatrix(initlocalmat);//cout << "當前局部" << endl;//showmatrix(localmat);cout << "初始位置" << endl;//cout << bone->getInitialPosition()<< endl;cout << bone->_getDerivedPosition() << endl;cout << "----------------------------" << endl;}得到每個關節(jié)相對根關節(jié)的位置:
時間: 0 關節(jié): head 初始位置 Vector3(2.33252, 15.4175, -53.7158) ---------------------------- 時間: 0 關節(jié): neck 初始位置 Vector3(2.38266, 14.9406, -53.5743) ---------------------------- 時間: 0 關節(jié): Rbicept 初始位置 Vector3(3.93105, 14.3725, -53.7863) ---------------------------- 時間: 0 關節(jié): Rforearm 初始位置 Vector3(5.03536, 11.6399, -53.6601) ---------------------------- 時間: 0 關節(jié): Rshoulder 初始位置 Vector3(2.33821, 14.3351, -53.6397) ---------------------------- 時間: 0 關節(jié): Lshoulder 初始位置 Vector3(2.34262, 14.3376, -53.6515) ---------------------------- 時間: 0 關節(jié): Lbicept 初始位置 Vector3(0.755036, 14.1863, -53.5923) ---------------------------- 時間: 0 關節(jié): Lforearm 初始位置 Vector3(-0.663156, 11.6248, -53.2319) ---------------------------- 時間: 0 關節(jié): Spine03 初始位置 Vector3(2.32999, 12.5657, -53.6904) ---------------------------- 時間: 0 關節(jié): Spine02 初始位置 Vector3(2.2534, 10.625, -53.7024) ---------------------------- 時間: 0 關節(jié): Spine01 初始位置 Vector3(2.17962, 8.75564, -53.7141) ---------------------------- 時間: 0 關節(jié): Rthigh 初始位置 Vector3(3.37061, 8.58021, -53.932) ---------------------------- 時間: 0 關節(jié): Rshin 初始位置 Vector3(2.73647, 3.857, -55.2306) ---------------------------- 時間: 0 關節(jié): Lthigh 初始位置 Vector3(0.923067, 8.93287, -53.5043) ---------------------------- 時間: 0 關節(jié): Lshin 初始位置 Vector3(1.13982, 4.04976, -54.2153) ---------------------------- 時間: 0 關節(jié): Lfoot 初始位置 Vector3(1.5438, -0.48033, -53.4399) ---------------------------- 時間: 0 關節(jié): Rfoot 初始位置 Vector3(3.29522, 1.13914, -51.5247) ---------------------------- 時間: 0 關節(jié): Ltoe 初始位置 Vector3(1.22996, -1.07427, -54.5649) ---------------------------- 時間: 0 關節(jié): Rtoe 初始位置 Vector3(3.09204, -0.0266533, -52.0873) ---------------------------- 時間: 0 關節(jié): Spineroot 初始位置 Vector3(2.17962, 8.75564, -53.7141) ----------------------------可以發(fā)現(xiàn)結果幾乎完全一樣,計算過程并未使用translate,只有在計算整個人體位置的時候涉及到, 上述代碼將人體位置設置為始終未(0,0,0)(0,0,0)(0,0,0)
#Translate作用
為了了解其作用, 這里在渲染代碼中書寫如下調試輸出
然后對比第一幀輸出
這樣我們就得到結論
人體當前位置=初始骨骼定義位置position+動畫幀定義的關節(jié)translate人體當前位置=初始骨骼定義位置position+動畫幀定義的關節(jié)translate 人體當前位置=初始骨骼定義位置position+動畫幀定義的關節(jié)translate
后記
本篇博客主要就是未后續(xù)的將任意的關節(jié)數(shù)據(jù)設置到OGRE中做準備, 重點就是要了解旋轉矩陣的到全局位置的計算方法。
以后想套入比如BVH或者ASF/AMC動捕數(shù)據(jù)的時候, 只需要保證初始骨骼姿態(tài)相同的情況下, 就可以將歐拉角轉換得到的旋轉矩陣應用到Ogre中,重點注意細節(jié)上的問題, 比如這個矩陣是否需要轉置或者翻轉后再運用之類的。
matlab仿真代碼:鏈接:https://pan.baidu.com/s/1kWUF3iZ 密碼:u792
Ogre仿真代碼:鏈接: https://pan.baidu.com/s/1uWEJ39pJcSvHvHSmTRD6_w 密碼: stmc
最后再貼一下可視化初始骨骼姿態(tài)結果(代碼都包含了)
Run運動第一幀的結果
總結
以上是生活随笔為你收集整理的【Ogre-windows】旋转矩阵及位置解析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 中信京东PLUS联名卡是小白卡吗?90%
- 下一篇: 征信花了怎么办信用卡?什么样的征信算花了