osgAnimation之动画基础篇

转载 2016年05月31日 20:49:08
  • 简介

osgAnimation是osg库中提供场景动画效果的一个类库,它为我们提供了许多与场景动画相关的类,比如关键帧、插值、采样、频道、骨骼动画、材质变化等。本课就对osgAnimation库中的基础类进行一些解析。以下都是我个人学习过程中的一些记录和体会,方便以后自己复习之用。

  • 开始

  • Keyframe

对应文件 osgAnimation/keyframe

学习osgAnimation库,首先需要理解关键帧的含义。关键帧顾名思义是对应某一时刻动画中的一种状态,就像制作动画片一样。我们知道早期的动画片是作者一页一页画出来的,在通过迅速地切换让我们感受到了动态的效果,在这里关键帧就相当于其中的一页画面。在osgAnimation中关键帧定义如下:

[cpp] view plain copy
  1. class Keyframe  
  2. {  
  3. public:  
  4.     double getTime() const { return _time; }  
  5.     void setTime(double time) { _time = time; }  
  6.   
  7. protected:  
  8.     double _time;  
  9.   
  10. };  
很简单是吧,你可能回想:这里面根本什么都没有啊!时间应该对应一个内容啊。由于对应的内容千变万化(可能是运动位置、颜色、角度等等),因此在派生类中采用模板的方式来处理,即:

[cpp] view plain copy
  1. template <class T>  
  2. class TemplateKeyframe : public Keyframe  
  3. {  
  4. protected:  
  5.     T _value;  
  6. public:  
  7.     TemplateKeyframe () {}  
  8.     ~TemplateKeyframe () {}  
  9.   
  10.     TemplateKeyframe (double time, const T& value)  
  11.     {  
  12.         _time = time;  
  13.         _value = value;  
  14.     }  
  15.   
  16.     void setValue(const T& value) { _value = value;}  
  17.     const T& getValue() const { return _value;}  
  18. };  
现在有了和时间对应的值,为了方便管理,还需要定义一个存储值的容器,即:KeyframeContainer,同样还是用相同的方式定义如下:

[cpp] view plain copy
  1. class KeyframeContainer : public osg::Referenced  
  2. {  
  3. public:  
  4.     KeyframeContainer() {}  
  5.     virtual unsigned int size() const = 0;  
  6. protected:  
  7.     ~KeyframeContainer() {}  
  8.     std::string _name;  
  9. };  
[cpp] view plain copy
  1. template <class T>  
  2. class TemplateKeyframeContainer : public std::vector<TemplateKeyframe<T> >, public KeyframeContainer  
  3. {  
  4. public:  
  5.     TemplateKeyframeContainer() {}  
  6.     typedef TemplateKeyframe<T> KeyType;  
  7.   
  8.     virtual unsigned int size() const { return (unsigned int)std::vector<TemplateKeyframe<T> >::size(); }  
  9. };  
可以看到该容器继承自std::vector,这样我们就可以采用push_back这样的方法往容器里面插入关键帧。

  • Interpolator

对应文件osgAnimation/Interpolator

有了关键帧之后,我们需要对关键帧之间的时间对应的值进行计算,这就是所谓的插值,定义插值的基类如下:

[cpp] view plain copy
  1.   template <class TYPE, class KEY>  
  2.   class TemplateInterpolatorBase  
  3.   {  
  4.   public:  
  5.   
  6. //KEY对应的是关键帧类型  
  7.       typedef KEY KeyframeType;  
  8. //TYPE对应的是关键帧对应的值  
  9.       typedef TYPE UsingType;  
  10.   
  11.   public:  
  12.       mutable int _lastKeyAccess;  
  13.   
  14.       TemplateInterpolatorBase() : _lastKeyAccess(-1) {}  
  15.   
  16.       void reset() { _lastKeyAccess = -1; }  
  17.   
  18. //通过时间time,计算出当前的索引值  
  19. //也就是该时间在两个关键帧之间  
  20.       int getKeyIndexFromTime(const TemplateKeyframeContainer<KEY>& keys, double time) const  
  21.       {  
  22.           int key_size = keys.size();  
  23.           if (!key_size) {  
  24.               osg::notify(osg::WARN) << "TemplateInterpolatorBase::getKeyIndexFromTime the container is empty, impossible to get key index from time" << std::endl;;  
  25.               return -1;  
  26.           }  
  27.           const TemplateKeyframe<KeyframeType>* keysVector = &keys.front();  
  28.           for (int i = 0; i < key_size-1; i++)  
  29.           {  
  30.               double time0 = keysVector[i].getTime();  
  31.               double time1 = keysVector[i+1].getTime();  
  32.   
  33.               if ( time >= time0 && time < time1 )  
  34.               {  
  35.                   _lastKeyAccess = i;  
  36.                   return i;  
  37.               }  
  38.           }  
  39.           return -1;  
  40.       }  
  41.   };  
这个基类负责寻找到插值所需要的两帧,也就是与需要插值时刻相距最近的那两个关键帧。之后进行插值就相对简单,osg中定义了几种插值的方式:

[cpp] view plain copy
  1. template <class TYPE, class KEY=TYPE>  
  2. class TemplateStepInterpolator : public TemplateInterpolatorBase<TYPE,KEY>  
  3. {  
  4. public:  
  5.   
  6.     TemplateStepInterpolator() {}  
  7.     void getValue(const TemplateKeyframeContainer<KEY>& keyframes, double time, TYPE& result) const  
  8.     {  
  9.   
  10.         if (time >= keyframes.back().getTime())  
  11.         {  
  12.             result = keyframes.back().getValue();  
  13.             return;  
  14.         }  
  15.         else if (time <= keyframes.front().getTime())  
  16.         {  
  17.             result = keyframes.front().getValue();  
  18.             return;  
  19.         }  
  20.   
  21.         int i = this->getKeyIndexFromTime(keyframes,time);  
  22.         result = keyframes[i].getValue();  
  23.     }  
  24. };  
StepInterpolator直接找到与time时刻相距最近那一帧的值,另外还有Linear(线性的插值)、SphericalLinear(球面的插值)、CubicBezier(贝塞尔插值)

  • Sampler

对应文件osgAnimation/Samper

有了关键帧和处理关键帧的插值算法,在osgAnimation中使用了Sampler(采样器)的方式将二者组合起来,其中的成员函数实现一目了然,都是调用插值器中的函数:

[cpp] view plain copy
  1.   //F实参化到时候需要传入的是一个Interpolator类  
  2. emplate <class F>  
  3.   class TemplateSampler : public Sampler  
  4.   {  
  5.   public:  
  6.   
  7. //KeyframeType关键帧的类型  
  8.       typedef typename F::KeyframeType KeyframeType;  
  9. //关键帧容器类型  
  10.       typedef TemplateKeyframeContainer<KeyframeType> KeyframeContainerType;  
  11.       //通过关键帧计算出的值的类型  
  12. typedef typename F::UsingType UsingType;  
  13.       typedef F FunctorType;  
  14.   
  15.       TemplateSampler() {}  
  16.       ~TemplateSampler() {}  
  17.   
  18.       void getValueAt(double time, UsingType& result) const { _functor.getValue(*_keyframes, time, result);}  
  19.       void setKeyframeContainer(KeyframeContainerType* kf) { _keyframes = kf;}  
  20.   
  21.       virtual KeyframeContainer* getKeyframeContainer() { return _keyframes.get(); }  
  22.       virtual const KeyframeContainer* getKeyframeContainer() const { return _keyframes.get();}  
  23.   
  24.       KeyframeContainerType* getKeyframeContainerTyped() { return _keyframes.get();}  
  25.       const KeyframeContainerType* getKeyframeContainerTyped() const { return _keyframes.get();}  
  26.   
  27. //安全地得到一个关键帧容器,建议在程序中使用该方法  
  28.       KeyframeContainerType* getOrCreateKeyframeContainer()  
  29.       {  
  30.           if (_keyframes != 0)  
  31.               return _keyframes.get();  
  32.           _keyframes = new KeyframeContainerType;  
  33.           return _keyframes.get();  
  34.       }  
  35.   
  36.       double getStartTime() const  
  37.       {  
  38.           if (!_keyframes || _keyframes->empty())  
  39.               return 0.0;  
  40.           return _keyframes->front().getTime();  
  41.       }  
  42.   
  43.       double getEndTime() const  
  44.       {  
  45.           if (!_keyframes || _keyframes->empty())  
  46.               return 0.0;  
  47.           return _keyframes->back().getTime();  
  48.       }  
  49.   
  50.   protected:  
  51.       FunctorType _functor;  
  52.       osg::ref_ptr<KeyframeContainerType> _keyframes;  
  53.   };  
到这里我们已经可以将采样器应用到我们的程序中了,例如:自己定义更新回调,传入关键帧参数,根据关键帧计算每个时刻的值(比如物体姿态),并进行更新来达到动画效果。在osgAnimation中还进行了更高的封装,即Channel(动画频道的概念)

  • Channel

对应文件osgAnimation/Channel和osgAnimation/Channel.cpp

在一个Channel之中封装了采样器Sampler和执行对象Target,执行对象可以理解为将采样器计算的插值结果保存在这个对象之中,查看一下Target的实现如下:

[cpp] view plain copy
  1. template <class T>  
  2.   class TemplateTarget : public Target  
  3.   {  
  4.   public:  
  5.   
  6.       inline void lerp(float t, const T& a, const T& b);  
  7.   
  8. //TODO:怎么解释?  
  9. // 以下是我的理解:  
  10.  //如果多个Channel共享一个执行对象Target,那么  
  11.  //在调用update的过程中,必须按照优先级的顺序进行  
  12.       void update(float weight, const T& val, int priority)  
  13.       {  
  14.           if (_weight || _priorityWeight)  
  15.           {  
  16.               if (_lastPriority != priority)  
  17.               {  
  18.                   _weight += _priorityWeight * (1.0 - _weight);  
  19.                   _priorityWeight = 0;  
  20.                   _lastPriority = priority;  
  21.               }  
  22.   
  23.               _priorityWeight += weight;  
  24.               float t = (1.0 - _weight) * weight / _priorityWeight;  
  25.               lerp(t, _target, val);  
  26.           }  
  27.           else  
  28.           {  
  29.               _priorityWeight = weight;  
  30.               _lastPriority = priority;  
  31.               _target = val;  
  32.           }  
  33.       }  
  34.       const T& getValue() const { return _target; }  
  35.       void setValue(const T& value) { _target = value; }  
  36.   protected:  
  37. //记录了最终的结果  
  38.       T _target;  
  39.   };  
在Channel的实现中有同样有一个update成员函数,它的实现反应了Channel的作用,通过采样器计算得到Value值,然后再通过Target对象的更新update,最终将计算得到的结构存储在Target对象的成员变量_target之中以便后续使用。代码如下:osgAnimation/Channel

[cpp] view plain copy
  1. virtual void update(double time, float weight, int priority)  
  2. {  
  3.     // skip if weight == 0  
  4.     if (weight < 1e-4)  
  5.         return;  
  6.     typename SamplerType::UsingType value;  
  7.     _sampler->getValueAt(time, value); //得到采样器插值的值value  
  8.     _target->update(weight, value, priority);//对value进行加权计算,并将结果保存在target对象之中  
  9. }  

  • Animation

对应文件osgAnimation/Animation和osgAnimation/Animation.cpp

最后将这些频道整合起来的类是动画类Animation,代码如下:

[cpp] view plain copy
  1. class OSGANIMATION_EXPORT Animation : public osg::Object  
  2. {  
  3. public:  
  4.     META_Object(osgAnimation, Animation)  
  5.   
  6.     Animation() : _duration(0), _weight(0), _startTime(0), _playmode(LOOP) {}  
  7.     Animation(const osgAnimation::Animation&, const osg::CopyOp&);  
  8.   
  9.     enum PlayMode  
  10.     {  
  11.         ONCE,  
  12.         STAY,  
  13.         LOOP,  
  14.         PPONG  
  15.     };  
  16.   
  17.     void addChannel (Channel* pChannel);  
  18.     ChannelList& getChannels();  
  19.     const ChannelList& getChannels() const;  
  20.   
  21.     void setDuration(double duration);  
  22.     void computeDuration();  
  23.     double getDuration() const;  
  24.   
  25.   
  26.     void setWeight (float weight);  
  27.     float getWeight() const;  
  28.   
  29.     bool update (double time, int priority = 0);  
  30.     void resetTargets();  
  31.   
  32.     void setPlayMode (PlayMode mode) { _playmode = mode; }  
  33.     PlayMode getPlayMode() const { return _playmode; }  
  34.   
  35.     void setStartTime(double time)  { _startTime = time;}  
  36.     double getStartTime() const { return _startTime;}  
  37.   
  38. protected:  
  39.     double computeDurationFromChannels() const;  
  40.     ~Animation() {}  
  41.   
  42.     double _duration;  
  43.     double _originalDuration;  
  44.     float _weight;  
  45.     double _startTime;  
  46.     PlayMode _playmode;  
  47.     ChannelList _channels;  
  48. };  
将许多Channel整合在了一起,实现的过程也是调用Channel中的成员函数来实现,很容易理解。在Animation中可以设置播放的模式,播放的模式实际上是通过这些模式来计算时间

[cpp] view plain copy
  1. switch (_playmode)  
  2. {  
  3. case ONCE:  
  4.     if (t > _originalDuration)  
  5.         return false;  
  6.     break;  
  7. case STAY:  
  8.     if (t > _originalDuration)  
  9.         t = _originalDuration;  
  10.     break;  
  11. case LOOP:  
  12.     if (!_originalDuration)  
  13.         t = _startTime;  
  14.     else if (t > _originalDuration)  
  15.         t = fmod(t, _originalDuration);  
  16.     //      std::cout << "t " << t << " duration " << _duration << std::endl;  
  17.     break;  
  18. case PPONG:  
  19.     if (!_originalDuration)  
  20.         t = _startTime;  
  21.     else  
  22.     {  
  23.         int tt = (int) (t / _originalDuration);  
  24.         t = fmod(t, _originalDuration);  
  25.         if (tt%2)  
  26.             t = _originalDuration - t;  
  27.     }  
  28.     break;  
  29. }  

以上就是osgAnimation库中基础部分的介绍,后续还会记录在实际操作中如何使用这些类来完成一个完整的动画。

转载地址:http://blog.csdn.net/csxiaoshui/article/details/22175055

相关文章推荐

OSG中显示模型自带的动画

假如三维模型自带动画,在OSG中,只需要得到模型的动画列表,然后从中选择一个动画,进行播放就可以了。而模型的动画列表,通常存放在UpdateCallBack中。 #include #includ...

OSG-场景动画基础知识_刚体动画

简单路径动画:    简单的路径动画不一定需要osgAnimation中丰富多彩的插值与关键帧采样机制,用户只需要输入某个对象节点在每个时刻的关键路径点、包括位置、旋转、缩放,就足以表达复杂的缸体动画...

OSG中的动画

OSG中的动画, AnimationPath, AnimationPathCallback, PositionAttitudeTransform的使用。 之前搞了两头牛了,但是它们都是静止的。现在我想...

osgAnimation之动画基础篇

简介 osgAnimation是osg库中提供场景动画效果的一个类库,它为我们提供了许多与场景动画相关的类,比如关键帧、插值、采样、频道、骨骼动画、材质变化等。本课就对osgAnimation库...

osganimationviewer例子

这个例子是一个简单的骨骼动画查看器。 "--drawbone"是否显示每个bone的坐标轴,通过AnimationManagerFinder获取到AnimationManagerBase 然后给了...
  • yungis
  • yungis
  • 2013年01月14日 07:47
  • 2322

osg示例解析之osganimationskinning(1)

简介 osgAnimationSkinning这个示例主要使用了osgAnimation中的骨骼蒙皮动画,关于骨骼蒙皮动画的介绍可以参考Skinned Mesh原理解析和一个最简单的实现示例这篇...

osg纹理动画效果

http://www.cnblogs.com/ylwn817/articles/1991614.html

OSG 关键帧动画

/* 1.创建一个AnimManager 一般继承于osg::NodeCallback 2.在AnimManager中创建一个采样器sampler(例如Vec3LinearSampler,OSG...

用cocos2d-x 实现UV动画--基础篇

用cocos2d-x 实现UV动画--基础篇 uv坐标与渲染 uv动画是指通过在程序运行时动态改变纹理坐标,实现动态效果的纹理动画,使用uv动画可以实现水流动,火焰燃烧等效果。 本文由liangn...

Android 自定义View动画篇之基础

前面学习了绘图相关知识,可以绘制复杂的图形了。但是面对如今越来越精美的App来说,显然还是不够的。怎么让我们的控件动起来,这是要考虑的问题。以前也学习过逐帧动画什么的,按以前的想法来说,是相当不错的效...
  • lcv587
  • lcv587
  • 2016年10月14日 23:43
  • 1853
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:osgAnimation之动画基础篇
举报原因:
原因补充:

(最多只允许输入30个字)