天天看點

關鍵幀系統的實作(Hermite位置插值+Squad四元數空間的朝向插值)

    談談工作中的低層運動控制方法吧。動畫師設計動畫時一般先設計好物體的運動軌迹,然後指定物體沿該軌迹的運動。物體的運動軌迹為樣條曲線,由使用者互動給出。     假設物體的運動軌迹為一空間參數曲線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樣條函數很多有講解,隻列出入切矢量和出切矢量的一般公式:

關鍵幀系統的實作(Hermite位置插值+Squad四元數空間的朝向插值)
關鍵幀系統的實作(Hermite位置插值+Squad四元數空間的朝向插值)

張量參數 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<CPoint3D*> 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:         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<double> m_seglength;                //for SetLengths()     vector<double> m_intervallength;           //for SetIntervalLengths()     double          m_totallength;     int             m_deltaU;   public:     vector<HermiteSegment*> m_curveseg;       bool    m_segmentsAreValid;     bool    m_lengthTableAreValid;   };     Spline為一抽象類,友善其他插值曲線擴充,例如B樣條等。定義了一個static vector<CPoint3D*> 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等人提出了一種構造機關四元數曲線的解析方法。它能把R 3中的曲線推廣到四元數空間,構造出與原始曲線具有類似微分性質的機關四元數曲線。    我在具體實作中使用的是squad方法插值曲線,效果圖如下,圖1定義了4個關鍵幀,圖2定義了6個關鍵幀。

關鍵幀系統的實作(Hermite位置插值+Squad四元數空間的朝向插值)
關鍵幀系統的實作(Hermite位置插值+Squad四元數空間的朝向插值)

基于文獻[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

繼續閱讀