`
chinamming
  • 浏览: 140628 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

Ogre骨骼动画分析

 
阅读更多

http://3dlearn.googlecode.com/files/ogre skeleton animation.pdf

欢迎指出文中错误

1 前言

骨骼蒙皮动画分两步骤进行:根据时间插值更新骨骼、然后根据骨骼更新每骨骼上的顶点。为了好玩,暂且这样看:在每一个时间点,对每一个骨骼,我们创建一个骨骼魔法,并将骨骼魔法施放到每一个骨头上;有个这些骨骼然后我们开始蒙皮,我们找出每一寸皮肤(一个顶点),并从骨堆里找出这块皮需要依附的骨头,当然骨头的数量都是有限的,一般就十几或翻倍的数量级,所以骨头还是比价好找的。我们将皮贴到骨头上,贴完所有的皮,我们就得到了一个骨头人鸟。看起来很形象:

下页示意图少儿不宜.

2 类图

<只能看pdf>

3 逻辑切片

不解释.

渲染

Root::renderOneFrame()

->Root::_updateAllRenderTargets

->RenderSystem::_updateAllRenderTargets()

->RenderWindow::update()

->D3D9RenderWindow::update(bool swap)

->RenderTarget::update()

->Viewport::update()

->Camera::_renderScene()

->SceneManager::_renderScene(Camera* camera, Viewport* vp, bool includeOverlays)

4 更新骨骼

只考虑线性插值更新骨骼的情况.

4.1 创建一个骨骼魔法

创建一根骨头需要的魔法。言归正传,其实就是创建一个TransformKeyFrame对象,看做一个全变换,一个变换只能应用到一个骨骼上。当前动作有24根骨头,在每帧里,你需要对着24根骨头施加24次骨骼魔法,如果美术认为男人应该是23根骨头少画了一根,可以不用纠结,知道这不是bug就行。

一个骨骼文件看起来像这样:

左边定义的是骨头,右边定义的是动作。恩,这里只有18根骨头,可以认为这个不是人类骨骼数据。在程序实现上,事实上考虑的术语叫joint,看起来像只是一个质点,可以这样理解,一个joint是一个空间射线,它表示了一个空间变化,也即一次旋转缩放平移。当然,它是一个矩阵,可以分解成一个平移和一个四元素变换。这时候似乎没有骨骼的长度,可以认为这个joint表示的只是骨头的关节处,骨头的长度隐藏在2个关节之间了。

右边描述的是动作,一个动作是所有的joint在时间轴上的一个个切片组成的。恩,为了便于组织数据,ogrejoint分类关键时间。其实也可以用关键时间来分类joint。恩,这其实也是一个很好的优化方式,如果关键帧分类下省略了joint,就表示这个joint不需要变换,其对应的顶点都不需要进行重新蒙皮计算了。例如一个人在挥手,假设全身只有手在挥舞,当然这动作应看起来像个僵尸。按ogre现在的实现,这个wave下的所有24(为了男女平等考虑男人和女人都是24根骨头)joint都必须有关键时间,就算关键时间少几处,也会将所有的joint进行插值。这个时候避开某个joint被蒙皮,只有在这个动作下删掉某个joint了。这彻头彻尾就是个机器人鸟。如果用改进的分类方式,在某个关键字里,可以省略一些joint,这样一个人边挥手边轻边摆头还是可以实现的。

创建一个骨头魔法分两步,第一部是取到当前时间点在关键帧中的插值系数,第二部是根据这个插值系数对这个骨头进行插值。

t=(i-k1)/(k2-k1)

可以看到移动和缩放非常好理解,都是进行的一次线性插值。只有旋转使用了四元素的归一化线性插值。两个旋转的插值似乎也只能用四元素插值,矩阵插值听说有这样那样的问题。这个插值有误差,并且不是恒速插值。

核心算法也是基本的线性插值公式,灰常神奇

q1+(q2-q1)*k

4.2 更新动画时间

AnalyzeAnimation.exe!AnalyzeAnimation::frameRenderingQueued

OgreMain_d.dll!Ogre::Root::_fireFrameRenderingQueued

OgreMain_d.dll!Ogre::Root::_fireFrameRenderingQueued

OgreMain_d.dll!Ogre::Root::_updateAllRenderTargets

OgreMain_d.dll!Ogre::Root::renderOneFrame

OgreMain_d.dll!Ogre::Root::startRendering

AnalyzeAnimation.exe!BaseApplication::go

AnalyzeAnimation.exe!WinMain

4.3 骨骼动画的核心玩法(更新骨骼)

OgreMain_d.dll!Ogre::NodeAnimationTrack::applyToNode

OgreMain_d.dll!Ogre::Animation::apply

OgreMain_d.dll!Ogre::Skeleton::setAnimationState

OgreMain_d.dll!Ogre::Entity::cacheBoneMatrices

OgreMain_d.dll!Ogre::Entity::updateAnimation

OgreMain_d.dll!Ogre::Entity::_updateRenderQueue

OgreMain_d.dll!Ogre::RenderQueue::processVisibleObject

OgreMain_d.dll!Ogre::SceneNode::_findVisibleObjects

OgreMain_d.dll!Ogre::SceneNode::_findVisibleObjects

OgreMain_d.dll!Ogre::SceneManager::_findVisibleObjects

OgreMain_d.dll!Ogre::SceneManager::_renderScene

OgreMain_d.dll!Ogre::Camera::_renderScene

OgreMain_d.dll!Ogre::Viewport::update

OgreMain_d.dll!Ogre::RenderTarget::_updateViewport

RenderSystem_Direct3D9_d.dll

OgreMain_d.dll!Ogre::RenderTarget::_updateAutoUpdatedViewports

OgreMain_d.dll!Ogre::RenderTarget::updateImpl

OgreMain_d.dll!Ogre::RenderTarget::update

OgreMain_d.dll!Ogre::RenderSystem::_updateAllRenderTargets

OgreMain_d.dll!Ogre::Root::_updateAllRenderTargets --->先更新帧监听,再更新实体

OgreMain_d.dll!Ogre::Root::renderOneFrame

OgreMain_d.dll!Ogre::Root::startRendering

AnalyzeAnimation.exe!BaseApplication::go

AnalyzeAnimation.exe!WinMain

TransformKeyFrame 看做一个全变换.

对骨骼(bone/node)进行变换的流程

输入:节点、时间(省略权值和缩放)

输出:节点的全变换

u构造出插值关键帧全变换buffer(TransformKeyFrame kf)

u从关键帧buffer释放一个平移buffer

u对节点施加平移buffer (省略权值与缩放)

u从关键帧buffer释放一个旋转buffer

u对节点施加旋转buffer

u从关键帧buffer释放一个缩放buffer

u对节点施加一个缩放buffer

每帧对每一个骨骼(这里蜕化成node)4个关键帧buffer,正是骨骼动画的核心玩法。

4.3.1 释放一个关键帧魔法

关键帧魔法需要创建一个特殊的buffer,即关键帧全变换buffer(TransformKeyFrame).

5 蒙皮

Ogre蒙皮算法的核心是对每顶点进行对应骨骼的全变换。

V=M4*V

分两步进行,第一步在Mesh::softwareVertexBlend中准备好计算数据结构的上下文,第二步在softwareVertexSkinning中进行每顶点的蒙皮计算。

处理软件索引顶点混合,本意是用于骨骼动画,但是也可用于其他用途.

const VertexData*

sourceVertexData

const VertexData*

targetVertexData

const Matrix4* const*

blendMatrices

size_t

numMatrices,

bool

blendNormals

sourceVertexData

顶点,法线,混合索引,混合权重

targetVertexData

目标的顶点,混合版本的法线缓存.需要注意向量的归一化.

blendMatrices

指向一个用于混合的矩阵数组,sourceVertexData的混合指数索引.

numMatrices

blendMatrices中矩阵数组的数量

blendNormals

true表示法线也同顶点一起混合.

srcElemPos

源顶点

srcElemNorm

源法线

srcElemBlendIndices

源混合索引

srcElemBlendWeights

源混合权重

srcPosBuf

源顶点缓存

srcIdxBuf

源索引缓存

srcWeightBuf

源权重缓存

srcNormBuf

源法线缓存

destElemPos

目顶点

destElemNorm

目法线

destPosStride

目法线跨步

5.2 蒙皮核心算法

核心算法如下

首先对顶点进行计算

ü找到当前的混合索引值

ü用这个值索引出混合矩阵M4

üM4左乘以顶点V1(*)得到V2

üV2进行加权计算得到V3(=V2*weight)

üV3归一处理得到V4(=V3.normalized)

然后对法线进行同样过程的计算,只是上面流程中的(*)处的V1换成法线.如果一个顶点存在多个权重值,需要对每一个权值重复上面的14步骤进行累积计算到V3.一次顶点计算完成,即对下一个顶点进行同样的计算过程.所有顶点计算完成,即完成了骨骼蒙皮.

http://3dlearn.googlecode.com/files/ogre skeleton animation.pdf

欢迎指出文中错误

1 前言

骨骼蒙皮动画分两步骤进行:根据时间插值更新骨骼、然后根据骨骼更新每骨骼上的顶点。为了好玩,暂且这样看:在每一个时间点,对每一个骨骼,我们创建一个骨骼魔法,并将骨骼魔法施放到每一个骨头上;有个这些骨骼然后我们开始蒙皮,我们找出每一寸皮肤(一个顶点),并从骨堆里找出这块皮需要依附的骨头,当然骨头的数量都是有限的,一般就十几或翻倍的数量级,所以骨头还是比价好找的。我们将皮贴到骨头上,贴完所有的皮,我们就得到了一个骨头人鸟。看起来很形象:

下页示意图少儿不宜.

2 类图

<只能看pdf>

3 逻辑切片

不解释.

渲染

Root::renderOneFrame()

->Root::_updateAllRenderTargets

->RenderSystem::_updateAllRenderTargets()

->RenderWindow::update()

->D3D9RenderWindow::update(bool swap)

->RenderTarget::update()

->Viewport::update()

->Camera::_renderScene()

->SceneManager::_renderScene(Camera* camera, Viewport* vp, bool includeOverlays)

4 更新骨骼

只考虑线性插值更新骨骼的情况.

4.1 创建一个骨骼魔法

创建一根骨头需要的魔法。言归正传,其实就是创建一个TransformKeyFrame对象,看做一个全变换,一个变换只能应用到一个骨骼上。当前动作有24根骨头,在每帧里,你需要对着24根骨头施加24次骨骼魔法,如果美术认为男人应该是23根骨头少画了一根,可以不用纠结,知道这不是bug就行。

一个骨骼文件看起来像这样:

左边定义的是骨头,右边定义的是动作。恩,这里只有18根骨头,可以认为这个不是人类骨骼数据。在程序实现上,事实上考虑的术语叫joint,看起来像只是一个质点,可以这样理解,一个joint是一个空间射线,它表示了一个空间变化,也即一次旋转缩放平移。当然,它是一个矩阵,可以分解成一个平移和一个四元素变换。这时候似乎没有骨骼的长度,可以认为这个joint表示的只是骨头的关节处,骨头的长度隐藏在2个关节之间了。

右边描述的是动作,一个动作是所有的joint在时间轴上的一个个切片组成的。恩,为了便于组织数据,ogrejoint分类关键时间。其实也可以用关键时间来分类joint。恩,这其实也是一个很好的优化方式,如果关键帧分类下省略了joint,就表示这个joint不需要变换,其对应的顶点都不需要进行重新蒙皮计算了。例如一个人在挥手,假设全身只有手在挥舞,当然这动作应看起来像个僵尸。按ogre现在的实现,这个wave下的所有24(为了男女平等考虑男人和女人都是24根骨头)joint都必须有关键时间,就算关键时间少几处,也会将所有的joint进行插值。这个时候避开某个joint被蒙皮,只有在这个动作下删掉某个joint了。这彻头彻尾就是个机器人鸟。如果用改进的分类方式,在某个关键字里,可以省略一些joint,这样一个人边挥手边轻边摆头还是可以实现的。

创建一个骨头魔法分两步,第一部是取到当前时间点在关键帧中的插值系数,第二部是根据这个插值系数对这个骨头进行插值。

t=(i-k1)/(k2-k1)

可以看到移动和缩放非常好理解,都是进行的一次线性插值。只有旋转使用了四元素的归一化线性插值。两个旋转的插值似乎也只能用四元素插值,矩阵插值听说有这样那样的问题。这个插值有误差,并且不是恒速插值。

核心算法也是基本的线性插值公式,灰常神奇

q1+(q2-q1)*k

4.2 更新动画时间

AnalyzeAnimation.exe!AnalyzeAnimation::frameRenderingQueued

OgreMain_d.dll!Ogre::Root::_fireFrameRenderingQueued

OgreMain_d.dll!Ogre::Root::_fireFrameRenderingQueued

OgreMain_d.dll!Ogre::Root::_updateAllRenderTargets

OgreMain_d.dll!Ogre::Root::renderOneFrame

OgreMain_d.dll!Ogre::Root::startRendering

AnalyzeAnimation.exe!BaseApplication::go

AnalyzeAnimation.exe!WinMain

4.3 骨骼动画的核心玩法(更新骨骼)

OgreMain_d.dll!Ogre::NodeAnimationTrack::applyToNode

OgreMain_d.dll!Ogre::Animation::apply

OgreMain_d.dll!Ogre::Skeleton::setAnimationState

OgreMain_d.dll!Ogre::Entity::cacheBoneMatrices

OgreMain_d.dll!Ogre::Entity::updateAnimation

OgreMain_d.dll!Ogre::Entity::_updateRenderQueue

OgreMain_d.dll!Ogre::RenderQueue::processVisibleObject

OgreMain_d.dll!Ogre::SceneNode::_findVisibleObjects

OgreMain_d.dll!Ogre::SceneNode::_findVisibleObjects

OgreMain_d.dll!Ogre::SceneManager::_findVisibleObjects

OgreMain_d.dll!Ogre::SceneManager::_renderScene

OgreMain_d.dll!Ogre::Camera::_renderScene

OgreMain_d.dll!Ogre::Viewport::update

OgreMain_d.dll!Ogre::RenderTarget::_updateViewport

RenderSystem_Direct3D9_d.dll

OgreMain_d.dll!Ogre::RenderTarget::_updateAutoUpdatedViewports

OgreMain_d.dll!Ogre::RenderTarget::updateImpl

OgreMain_d.dll!Ogre::RenderTarget::update

OgreMain_d.dll!Ogre::RenderSystem::_updateAllRenderTargets

OgreMain_d.dll!Ogre::Root::_updateAllRenderTargets --->先更新帧监听,再更新实体

OgreMain_d.dll!Ogre::Root::renderOneFrame

OgreMain_d.dll!Ogre::Root::startRendering

AnalyzeAnimation.exe!BaseApplication::go

AnalyzeAnimation.exe!WinMain

TransformKeyFrame 看做一个全变换.

对骨骼(bone/node)进行变换的流程

输入:节点、时间(省略权值和缩放)

输出:节点的全变换

u构造出插值关键帧全变换buffer(TransformKeyFrame kf)

u从关键帧buffer释放一个平移buffer

u对节点施加平移buffer (省略权值与缩放)

u从关键帧buffer释放一个旋转buffer

u对节点施加旋转buffer

u从关键帧buffer释放一个缩放buffer

u对节点施加一个缩放buffer

每帧对每一个骨骼(这里蜕化成node)4个关键帧buffer,正是骨骼动画的核心玩法。

4.3.1 释放一个关键帧魔法

关键帧魔法需要创建一个特殊的buffer,即关键帧全变换buffer(TransformKeyFrame).

5 蒙皮

Ogre蒙皮算法的核心是对每顶点进行对应骨骼的全变换。

V=M4*V

分两步进行,第一步在Mesh::softwareVertexBlend中准备好计算数据结构的上下文,第二步在softwareVertexSkinning中进行每顶点的蒙皮计算。

处理软件索引顶点混合,本意是用于骨骼动画,但是也可用于其他用途.

const VertexData*

sourceVertexData

const VertexData*

targetVertexData

const Matrix4* const*

blendMatrices

size_t

numMatrices,

bool

blendNormals

sourceVertexData

顶点,法线,混合索引,混合权重

targetVertexData

目标的顶点,混合版本的法线缓存.需要注意向量的归一化.

blendMatrices

指向一个用于混合的矩阵数组,sourceVertexData的混合指数索引.

numMatrices

blendMatrices中矩阵数组的数量

blendNormals

true表示法线也同顶点一起混合.

srcElemPos

源顶点

srcElemNorm

源法线

srcElemBlendIndices

源混合索引

srcElemBlendWeights

源混合权重

srcPosBuf

源顶点缓存

srcIdxBuf

源索引缓存

srcWeightBuf

源权重缓存

srcNormBuf

源法线缓存

destElemPos

目顶点

destElemNorm

目法线

destPosStride

目法线跨步

5.2 蒙皮核心算法

核心算法如下

首先对顶点进行计算

ü找到当前的混合索引值

ü用这个值索引出混合矩阵M4

üM4左乘以顶点V1(*)得到V2

üV2进行加权计算得到V3(=V2*weight)

üV3归一处理得到V4(=V3.normalized)

然后对法线进行同样过程的计算,只是上面流程中的(*)处的V1换成法线.如果一个顶点存在多个权重值,需要对每一个权值重复上面的14步骤进行累积计算到V3.一次顶点计算完成,即对下一个顶点进行同样的计算过程.所有顶点计算完成,即完成了骨骼蒙皮.

分享到:
评论

相关推荐

    论文研究-基于BVH驱动的OGRE骨骼动画.pdf

    OGRE游戏引擎中的角色动画将模型与骨骼文件进行映射。若想在不借助第三方建模软件重新制作模型和动画的基础上更改现有模型的动作,获取与模型匹配的骨骼数据成为一个难题。提出了一种将标准BVH动作文件解析为XML文件...

    骨骼动画例子附源代码

    非常不错的一个例子.附有源代码,供大家学习.快下载吧,还等什么.

    Ogre 引擎 源码分析

    对Ogre引擎的源码进行分析,从消息,文件,数据,场景渲染等方面进行了详细的介绍。

    ogre 渲染系统分析

    ogre 渲染系统分析,非常不错的资料,讲的很详细

    ogre 文件系统分析

    ogre 文件系统分析,非常不错的资料,讲的很详细

    OGRE场景管理分析

    场景组织是整个 Engine的灵魂, 而且到目前为止没有适用于任何场景的场景组织方式,所以都是以 Abstract Factory方式进行组织,然后根据不同的场景采用不同的场景组织方式,再进行绘制...本文从这些基本特性进行分析。

    一个ogre动画演示实例

    ogre动画演示实例,源代码,轻松学习ogre

    ogre模型下载

    ogre骨骼动画模型,是一个忍者的骨骼动画!

    3D渲染引擎OGRE源码分析

    OGRE源码分析 对引擎中的主要类进行分析

    OGRE设计模式分析

    OGRE的设计结构十分清晰,这得归功于设计模式的成功运用。 本文档详细分析了OGRE的设计模式。

    ogre场景组织分析

    ogre场景组织分析

    ogre源码分析与使用指南

    详细分析了ogre的源码,对里面的函数进行了系统的讲解,是ogre初学者不可多得的好资料,欢迎下载!

    ogre3D引擎教程

    OGRE分析之场景管理.pdf OGRE分析之场景渲染.pdf OGRE分析之设计模式(1234).pdf OGRE分析之文件系统(1234).pdf ogre数据文件结构分析.pdf ogre文件系统分析.pdf 基于HLA的OGRE引擎的实现及应用研究.kdh 基于OGRE...

    Ogre引擎分析,OgreRenderingSystem

    Ogre引擎分析,OgreRenderingSystem分析

    OGRE引擎分析汇总

    对Ogre引擎的源码进行分析,对OGRE设计模式,消息,文件,材质,数据文件结构,场景,渲染等方面进行了介绍。

    ogre 材质分析

    ogre 材质系统分析,非常不错的资料,讲的很详细

    Ogre引擎分析

    Ogre引擎分析,帮助您更加深入的了解Ogre3D图形渲染引擎,无论您是刚接触Ogre还是已经对Ogre很熟悉,都值得一看。

    ogre 开发文档合集

    《ogre场景组织分析.pdf》 《OGRE的消息机制.pdf》 《Ogre的渲染系统(Rendering System).pdf》 《OGRE使用指南v0.01a.pdf》 《ogre数据文件结构分析.pdf》 《ogre文件系统分析.pdf》 《OGRE周边版块.pdf》

    ogre 数据文件分析

    ogre 数据文件分析,非常不错的资料,讲的很详细

    OGRE 动画源码解析及动画创建步骤

    学习ogre非常好的资料,本人从网上搜集下来,特贡献出来供大家分享,此文章对我的学习帮助很大,希望对大家有所帮助.

Global site tag (gtag.js) - Google Analytics