Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1322120
  • 博文数量: 281
  • 博客积分: 8800
  • 博客等级: 中将
  • 技术积分: 3345
  • 用 户 组: 普通用户
  • 注册时间: 2006-05-17 22:31
文章分类

全部博文(281)

文章存档

2013年(1)

2012年(18)

2011年(16)

2010年(44)

2009年(86)

2008年(41)

2007年(10)

2006年(65)

我的朋友

分类: LINUX

2011-12-18 20:17:05

谈谈工作中的低层运动控制方法吧。动画师设计动画时一般先设计好物体的运动轨迹,然后指定物体沿该轨迹的运动。物体的运动轨迹为样条曲线,由用户交互给出。
    假设物体的运动轨迹为一空间参数曲线Q(u),我们必须对Q(u)等间隔采样,以求得物体在每一帧的位置。但是,当对参数u等间隔采样时,并不能得到样条曲线上的等间隔采样,因为等间距的参数不一定对应等间距的弧长。由于弧长函数s=A(u)是u的严格增函数,因此s和u是一对一的关系。A(u)是一个积分方程,它没有解析解。我们无法直接将A-1(s)解析表达出来,通常只能采用数值求解的方法。此方法在《Advanced Animation and Rendering Techniques》中有详细说明,此书作者Watt还根据他们的实践提出了一种基于向前差分的近似计算方法,这在算法中均以实现。而我们的轨迹用什么样条的类型来表示呢?计算机辅助几何设计中常用的样条函数有Bezier、B样条、β样条,各有优缺点吧。B样条是动画中较常用的曲线之一,其二阶连续性保证了运动的光滑性,局部性保证了可对动画进行局部调整,因此非常适合于作轨迹曲线,但并不太适合关键帧插值。在Hermite函数基础上Kochanek将两个附加参数引入到约束方程中,得到的样条函数非常适合关键帧插值系统。
    具体的Hermite样条函数很多有讲解,只列出入切矢量和出切矢量的一般公式:
张量参数t用来控制曲线在插值点处的尖锐性;偏移量参数b用来调整源弦和目标弦的相对权值;连续性参数c来控制左右切向的大小。利用这三个参数非常适合于指定曲线的形状调整。
    关于位置插值曲线的设计:
class Spline 
{
public:
    Spline();
    virtual ~Spline();
 
public:
    void AddKeyPoint(CPoint3D* const pt);
    void RemovePoint(CPoint3D* const pt);
 
    //return the point of the arc length being s(0<= s <=1)
    virtual CPoint3D ArcLengthPoint(double s) = 0;
    //The method based on forward difference approximately
    virtual CPoint3D ArcIntervalLengthPoint(double s) = 0;
 
    //Build the segments according to KeyPoints, whose spline type may be Hermite,
    //Bspline, Bezier inherited from Spline class
    virtual void    BuildSegment() = 0;
    //Update these KeyPoint values when data are changed
    virtual void    UpdateSplineValues() = 0;
 
protected:
 
public:
    static vector m_splineKeyPoint;
 
private:
    Spline(const Spline& s);
    Spline& operator =(const Spline& s);
 
};
 
class HermiteSpline : public Spline 
{
public:
    HermiteSpline();
    virtual ~HermiteSpline();
 
public:
    void    SetLengths();       //build a table of u against accumulated arclength
    virtual CPoint3D ArcLengthPoint(double s);
 
    void    SetIntervalLengths();        //a method based on forward differences
    virtual CPoint3D ArcIntervalLengthPoint(double s);
 
    virtual void    BuildSegment();
 
    virtual void    UpdateSplineValues();
 
private:
    /*define the hermite segment between p1 and p2(have two keypoints at least)
    construct the segment using four vector (P1, P2, DD1, DS2)
    */
    class HermiteSegment
    {
    public:
        virtual ~HermiteSegment() {};
 
        HermiteSegment(const CPoint3D* const p0, const CPoint3D* const p1, const CPoint3D* const p2, const CPoint3D* const p3);
 
    private:
        HermiteSegment();
        HermiteSegment(const HermiteSegment& segment);
        HermiteSegment& operator =(const HermiteSegment& segment);
 
    public:
        //tension   [-1, 1] default value is 0.0,可控制切矢量Ti的大小
        float   tension() const { return m_tension; }
        //bias [-1, 1], default value is 0.0,可控制曲线在Pi处的切向方向
        float   bias() const { return m_bias; }
        //continuity [-1, 1], default value is 0.0, 可控制左右切向的大小
        float   continuity() const { return m_continuity; }
        //curve resolution, default is 10
        int     resolution() const { return m_resolution; }
 
        void    SetTension(float t) { m_tension = t; }
        void    SetBias(float b) { m_bias = b; }
        void    SetContinuity(float c) { m_continuity = c; }
        void    SetResolution(int res) { m_resolution = res; }
 
    public:
        float       ArcLength(float ustart, float uend);
        CPoint3D    GetPointOnSegment(float u);
 
    private:
        void    SetCoef();
        float   ArcIntegrand(float u);
 
    private:
        CPoint3D    m_keypoint[4];
        float       m_coef[4][3];
 
        float   m_bias;
        float   m_tension;
        float   m_continuity;
        int     m_resolution;
 
        bool    m_coefIsValid;
    };
       
    vector m_seglength;                //for SetLengths()
    vector m_intervallength;           //for SetIntervalLengths()
    double          m_totallength;
    int             m_deltaU;
 
public:
    vector m_curveseg;
 
    bool    m_segmentsAreValid;
    bool    m_lengthTableAreValid;
 
};
    Spline为一抽象类,方便其他插值曲线扩展,例如B样条等。定义了一个static vector m_splineKeyPoint;为插值曲线类所共有。
 
    关键帧插值问题实际上可分为位置插值和朝向插值两个子问题。对物体朝向的表示我们使用四元数(quaternion)表示。Quaternion的基本概念及应用《Advanced Animation and Rendering Techniques》也有涉及。最重要的概念是单位四元数空间的球面线性插值函数Slerp。从q1到q2的球面线性插值函数如下:
CQuaternion CQuaternion::slerp(const CQuaternion& q1, const CQuaternion& q2, const double t, bool allowFlip)
 
 
    如何用四元数构造物体朝向的插值呢?Shoemake用球面线性插值取代普通的线性插值,将这种几何构造方法推广到四维超球面上,从而来构造四维超球面上的三次样条。
CQuaternion CQuaternion::squad(const CQuaternion& a, const CQuaternion& tgA, const CQuaternion& tgB, const CQuaternion& b, double t)
 
   Squad是使用的较普遍的四元数插值得的构造方式。1995年,Kim等人提出了一种构造单位四元数曲线的解析方法。它能把R3中的曲线推广到四元数空间,构造出与原始曲线具有类似微分性质的单位四元数曲线。
   我在具体实现中使用的是squad方法插值曲线,效果图如下,图1定义了4个关键帧,图2定义了6个关键帧。
 
基于文献[2]中的具有高阶导数的单位四元数曲线的一般构造方法以后再实现吧,把四元数函数的导数和角速度搞清楚也不是很难的^_^
See also:
1、《Advanced Animation and Rendering Techniques》
2、Kim M J, Kim M S, Shin S Y. A general construction scheme for unit quaternion curves with simple high order derivatives. Computer Graphics, 1995, 29(3):369-376
阅读(3292) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~