日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Ogre共享骨骼与两种骨骼驱动方法

發布時間:2023/12/13 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Ogre共享骨骼与两种骨骼驱动方法 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前言

最近業務中用到Ogre做基于3D關鍵點虛擬角色骨骼驅動,但是遇到兩個問題:

  • 身體、頭、眼睛、衣服等mesh的骨骼是分開的,但是骨骼結構都是一樣的,需要設置共享骨骼
  • 驅動的時候可以直接修改骨骼旋轉量,或者將旋轉量存到動畫幀里面去,后者會根據播放時間間隔自動插幀

國際慣例,參考博客:

Ogre3D 實現角色換裝

【Ogre-windows】旋轉矩陣及位置解析

Ogre 換裝系統 shareSkeletonInstanceWith

代碼實現

下面分別包括:共享骨骼、關節驅動、動畫幀驅動、遇到的坑

其中關節驅動和動畫幀驅動方法所創建的運動為左小腿伸直彎曲,再伸直彎曲,再回到伸直彎曲,如此反復。

共享骨骼

核心函數是shareSkeletonInstanceWith,能夠指定將誰的骨骼共享給誰

但是需要注意,共享與被共享的骨骼具有同樣的拓撲結構,不然會報錯。

如果想強制共享,那就需要使用_notifySkeleton,官方描述如下:

Internal notification, used to tell the Mesh which Skeleton to use without loading it. @remarks This is only here for unusual situation where you want to manually set up a Skeleton. Best to let OGRE deal with this, don't call it yourself unless you really know what you're doing.

意思就是說,告訴一個mesh用另一個骨骼,但是不要輕易去用它,因為很容易出現問題,待會實驗就知道了。

額外代碼就不貼了,源碼看文末就行。

首先讀取三個模型:兩個Sinbad.mesh和一個jaiqua.mesh

//主模型 ent = scnMgr->createEntity("Sinbad.mesh"); SceneNode* node = scnMgr->getRootSceneNode()->createChildSceneNode(); node->attachObject(ent); // 副模型1 ent1 = scnMgr->createEntity("jaiqua.mesh"); SceneNode* node1 = node->createChildSceneNode(); node1->setPosition(10, 0, 0); node1->attachObject(ent1);//副模型2 ent2 = scnMgr->createEntity("Sinbad.mesh"); SceneNode* node2 = node->createChildSceneNode(); node2->setPosition(-10, 0, 0); node2->attachObject(ent2);

然后共享骨骼:

ent1->shareSkeletonInstanceWith(ent); ent2->shareSkeletonInstanceWith(ent);

會發現報錯:

case Exception::ERR_RT_ASSERTION_FAILED: throw RuntimeAssertionException(number, desc, src, file, line);

就是因為jaiqua.mesh與Sinbad.mesh的骨骼不一樣,所以對于jaiqua.mesh必須增加:

ent1->getMesh()->_notifySkeleton(const_cast<SkeletonPtr&>(ent->getMesh()->getSkeleton()) );

如此便能成功運行了,如下圖所示,左到右分別是:副模型2、主模型、副模型1;由于副模型1和主模型具有不同的骨骼,所以無法正常驅動。

共享骨骼的作用就在于:有時候同一個模型,分成了幾部分設計,比如頭和身體是分開的,便于將表情驅動和肢體驅動分開,但是它倆在設計的時候都是完整的人體骨骼,所以需要共享骨骼做一個同步。

修改關節旋轉的驅動

動畫幀驅動方法

分為兩種,一種是一邊創建一邊播放,另一種是創建完畢再播放

先創建再播放

首先要知道你想創建的動畫時長、幀率、播放速度,我這里為了測試幀的插值效果,創建了6s的動畫幀序列,首先初始化:

anim = skel->createAnimation("myanim", 6); anim->setInterpolationMode(Animation::IM_SPLINE); tracksnew = anim->createNodeTrack(lknee->getHandle(), lknee); createAnim(); //創建動畫幀//animation play as = ent->getAnimationState("myanim"); as->setEnabled(true); as->setLoop(false);

接下來就是創建動畫幀,具體的創建方法,在之前的博客已經介紹過,這里直接貼代碼:

void MyTestApp::createAnim() {for (int i = 0; i < 6; i++) {TransformKeyFrame *newKF = tracksnew->createNodeKeyFrame(i);Quaternion quat;quat.FromAngleAxis(Degree(i%2? 0.0f: -90.0f), Vector3::UNIT_X);newKF->setRotation( quat);prev_rotate = quat;}ent->refreshAvailableAnimationState(); }

注意創建完畢,要刷新一下動畫的狀態,不然修改無法生效。

最后在frameRenderingQueued里面設置一下播放間隔:

as->addTime(0.033333);

表示每次播放接下來的0.0333幀數據,如果沒有,就會自動插值出來。

一邊創建一邊播放

同樣先在setup里面初始化動畫,但是記得刷新

// create animation anim = skel->createAnimation("myanim", 6); anim->setInterpolationMode(Animation::IM_SPLINE); tracksnew = anim->createNodeTrack(lknee->getHandle(), lknee); ent->refreshAvailableAnimationState();//animation as = ent->getAnimationState("myanim"); as->setEnabled(true); as->setLoop(false);

接下來直接在渲染主線程里面去寫入動畫幀,一邊渲染一邊寫

// frame rendering int i = 0; bool MyTestApp::frameRenderingQueued(const FrameEvent &evt){ i++;TransformKeyFrame *newKF = tracksnew->createNodeKeyFrame(i);Quaternion quat;quat.FromAngleAxis(Degree(i%2? 0.0f: -90.0f), Vector3::UNIT_X);newKF->setRotation(quat);ent->refreshAvailableAnimationState();std::cout << as->getTimePosition() << std::endl;as->addTime(0.033333);return true; }

這里需要注意一個問題,渲染是從第0幀開始的,但是你直接修改第0幀,這個數值在渲染進行結束前是無法生效的,也就是說在渲染線程里面修改的幀必須在當前幀渲染完畢才能生效,所以你修改的幀必須在當前渲染幀的后面,所以上述代碼,直接修改的第1幀,并不是跟先創建動畫后播放一樣修改的第0幀。

直接修改關節旋轉

非常簡單,跟創建動畫序列無任何關系,只需要在setup中,將相關關節的setManuallyControlled設置為true

SkeletonInstance *skel = ent->getSkeleton(); lshoulder = skel->getBone("Humerus.L"); lshoulder->setManuallyControlled(true); lknee = skel->getBone("Calf.L"); lknee->setManuallyControlled(true);

然后再在渲染線程中修改骨骼旋轉

int i = 0; bool MyTestApp::frameRenderingQueued(const FrameEvent &evt){ i++;Quaternion quat;quat.FromAngleAxis(Degree(i%2? 0.0f: -90.0f), Vector3::UNIT_X);lknee->setOrientation(quat);return true; }

因為這個渲染速度太快了,所以必須用斷點才能看清每一幀的驅動效果,視頻后半段是取消斷點,一直驅動的結果

很容易發現,這種方法雖然簡單,但是共享骨骼會失效,所以一旦使用此種方法驅動兩套一樣的骨骼,必須手動同步,把兩套骨骼的所有關節setManuallyControlled設置為true,記住要刪掉共享骨骼的代碼先

for (int j = 0; j < skel->getNumBones(); j++) {skel->getBone(j)->setManuallyControlled(true);skel2->getBone(j)->setManuallyControlled(true);}

然后每次修改,都要同步每個關節遍歷一遍,將兩個骨骼對應關節同步好

for (int j = 0; j < skel->getNumBones(); j++) { skel2->getBone(j)->setOrientation(skel->getBone(j)->getOrientation());}

這樣就可以同步運動啦,同樣沒有幀間平滑

注意坑

一定不要在幀動畫驅動方法中,將骨骼的setManuallyControlled設置為true了,不然每一幀都是基于上一幀的結果驅動,正常的骨骼動畫應該是類似于BVH動畫,每一幀都應該是獨立的,且基于初始姿態的變換,比如A-pos或者T-pos,假設動畫幀驅動的方法開啟了手動控制,那么動畫結果就是:

后記

本篇博文記錄了工作中遇到了多個骨骼共享同一套動作的方法,同時這種方法都支持實時驅動,比如通過3D關鍵點計算得到旋轉量以后,立馬渲染出來。

后續應該會更新unity和Unreal Engine里面的肢體驅動方法,主要是將引擎與python通過socket通信傳遞深度學習提取的3D關鍵點,然后使用FABRIK或者其它動力學方法驅動虛擬角色,有興趣可以關注一下。

本博文同步更新到微信公眾號中,有興趣可關注一波,代碼在微信公眾號簡介的github找得到,有問題直接公眾號私信。

總結

以上是生活随笔為你收集整理的Ogre共享骨骼与两种骨骼驱动方法的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。