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.meshSinbad.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找得到,有问题直接公众号私信。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/246567.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

仿射变换和透视变换

前言 在前面做换脸的博客中提到了使用仿射变换和透视变换将两张不同的人脸基于关键点进行对齐&#xff0c;保证一张人脸贴到另一张人脸时&#xff0c;大小完全一致&#xff1b;所以有必要理解一下这两个概念的区别&#xff0c;由于以实用性为目的&#xff0c;所以所有的图像算…

obj格式解析

前言 最近处理一些网格渲染的时候&#xff0c;需要解析Obj文件&#xff0c;从Free3D上随便找了个免费的人体obj模型解析测试一波 国际惯例&#xff0c;参考博客&#xff1a; 本文所使用的从Free3D下载的模型 .obj文件格式与.mtl文件格式 详解3D中的obj文件格式 3D中OBJ文…

Flask服务部署与简单内网穿透

前言 最近学习部署的时候&#xff0c;想到深度学习里面通常用的部署方法是flask做服务端&#xff0c;然后使用nginx做负载均衡&#xff0c;貌似也能做内网穿透。不过我不太懂负载均衡&#xff0c;只想利用本地电脑搭建一个简单的服务器&#xff0c;实现外部调用API服务的功能。…

OpenCV学习——轮廓检测

前言 轮廓检测是传统视觉中非常常用的功能&#xff0c;这里简单记录一下opencv中的轮廓检测算法使用方法&#xff0c;至于理论&#xff0c;后续有机会再去细品。 国际惯例&#xff1a; OpenCV官方的轮廓检测教程python版 OpenCV中的二值化方法教程 OpenCV轮廓层级官方文档…

RBF神经网络理论与实现

前言 最近发现有挺多人喜欢径向基函数(Radial Basis Function,RBF)神经网络&#xff0c;其实它就是将RBF作为神经网络层间的一种连接方式而已。这里做一个简单的描述和找了个代码解读。 之前也写过一篇&#xff0c;不过排版不好看&#xff0c;可以戳这里跳转 国际惯例&#x…

基于python和unity交互的卡通角色肢体和表情驱动(深度学习)

前言 最近看到了好多卡通角色的肢体驱动的东东&#xff0c;感觉是时候发挥一下读研时候学的东西了&#xff0c;而且虽然现在不炼丹了&#xff0c;但是还是得保持吃丹的技能。这个项目找了很多很多代码进行测试&#xff0c;最终集成了一个3D姿态估计和人脸关键点提取的代码。 …

OpenCV学习——形态学

前言 继续学习图像里面的形态学知识——结构元、腐蚀、膨胀、开运算、闭运算、击中/不击中变换。以及部分基本形态学算法&#xff0c;包括边界提取、空洞填充、连通分量的提取、凸壳、细化、粗化、骨架、裁剪、形态学重建。 其实就是对冈萨雷斯的《数字图像处理》中第9章节《…

径向基函数RBF三维网格变形

前言 之前写过径向基函数(RBF)神经网络做分类或者拟合。然后挖了个坑说在《Phase-Functioned Neural Networks for Character Control》里面提到了用于做地形编辑&#xff0c;所以这篇博客就是解析一下如何用RBF做网格编辑系统。 参考博客&#xff1a; Noe’s tutorial on d…

OBJ可视化——UV还原(修正)

前言 前面写过一篇obj格式解析的博客&#xff0c;但是这篇文章中可视化的工作是参考PRNet的源码进行的&#xff0c;后来细细思考了一下&#xff0c;有点问题&#xff0c;具体看下面。 问题来源 在PRNet源码的render.py中有个函数render_texture&#xff0c;是作者用于将uv展…

Unity中BVH骨骼动画驱动的可视化理论与实现

前言 找了很久使用BVH到unity中驱动骨骼动画的代码&#xff0c;但是都不是特别好用&#xff0c;自己以前写过&#xff0c;原理很简单&#xff0c;这里记录一下。 理论 初始姿态 在BVH或者其它骨骼动画中&#xff0c;一般涉及到三种姿势&#xff1a;A-pose&#xff0c;T-pos…

卡通驱动项目ThreeDPoseTracker——模型驱动解析

前言 之前解析过ThreeDPoseTracker这个项目中的深度学习模型&#xff0c;公众号有兄弟私信一些问题&#xff0c;我刚好对这个项目实现有兴趣&#xff0c;就分析一波源码&#xff0c;顺便把问题解答一下。 这个源码其实包括很多内容&#xff1a;3D姿态估计&#xff0c;坐标平滑…

卡通驱动项目ThreeDPoseTracker——关键点平滑方案解析

前言 之前对ThreeDPoseTracker的深度学习模型和unity中的驱动方法进行过解析&#xff0c;还有一个比较重要的就是从深度学习模型出来的3D关键点数据会有抖动&#xff0c;在ThreeDPoseTracker源码中有做两次平滑&#xff0c;一部分是卡尔曼滤波&#xff0c;还有一部分是低通滤波…

卡通角色表情驱动系列一

前言 分析完ThreeDPoseTracker来做卡通角色的身体驱动&#xff0c;接下来在卡通驱动领域还有一个是表情驱动。对这个真的是一窍不通啊&#xff0c;只能慢慢看论文了。 国际惯例&#xff0c;参考博客/论文&#xff1a; 《Landmark-guided deformation transfer of template f…

opencv相机标定和人头姿态估计案例

前言 头部驱动除了之前关注的表情驱动外&#xff0c;还有眼球驱动和头部方向驱动。本博客基于opencv官方文档和部分开源代码来研究如何基于人脸关键点获取头部的朝向。 国际惯例&#xff0c;参考博客&#xff1a; opencv:Camera Calibration and 3D Reconstruction opencv:…

卡通角色表情驱动系列二

前言 之前介绍了使用传统算法求解BS系数的表情驱动方法&#xff0c;其中提到过的三种方法之一是基于网格形变迁移做的&#xff0c;那么这篇文章就是对《Deformation Transfer for Triangle Meshes》做表情驱动的解析。 国际惯例&#xff0c;参考博客&#xff1a; 论文原文《…

UE自带重定向原理

UE自带重定向方法验证 核心源码在VS的解决方案中的位置&#xff1a; UE4\Source\Developer\AssetTools\Private\AssetTypeActions\AnimSequence.cpp中第3237行RemapTracksToNewSkeleton函数 跳转方法 AssetTypeActions_AnimationAsset.cpp的RetargetNonSkeletonAnimationHa…

【caffe-Windows】caffe+VS2013+Windows无GPU快速配置教程

前言 首先来一波地址&#xff1a; happynear大神的第三方caffe&#xff1a;http://blog.csdn.net/happynear/article/details/45372231 Neil Z大神的第三方caffe&#xff1a;https://initialneil.wordpress.com/2015/01/11/build-caffe-in-windows-with-visual-studio-2013-…

【caffe-Windows】caffe+VS2013+Windows+GPU配置+cifar使用

前言 国际惯例&#xff0c;先来波地址&#xff1a; CUDA WIN7&#xff1a;链接&#xff1a;http://pan.baidu.com/s/1nvyA3Qp 密码&#xff1a;h0f3 官方网址&#xff1a;https://developer.nvidia.com/cuda-toolkit CUDA WIN10:链接&#xff1a;http://pan.baidu.com/s/1…

【一些网站的收集】包含机器学习深度学习大牛主页等

数学概念部分 旋转矩阵、欧拉角、四元数的比较 欧拉角和四元数的表示 四元数与旋转 B样条曲线 非常好的概率统计学习的主页 误差方差偏差 编程语言学习 C#编程视频 OpenGL编程NeHe OpenGL官网 OpenGL“我叫MT“纯手工3D动画制作之1——基础介绍 【强大】非常好的Op…

Eureka源码分析

Eureka源码分析 Eureka server入口: Spring.factories PS: 意味着如果加载EurekaServerAutoConfiguration成功,需要 ConditionalOnBean(EurekaServerMarkerConfiguration.Marker.class)需加载成功. 通过Bean注入了很多类 本质上, eureka-server包含很多事件: EurekaInstanceC…