谈谈工作中的低层运动控制方法吧。动画师设计动画时一般先设计好物体的运动轨迹,然后指定物体沿该轨迹的运动。物体的运动轨迹为样条曲线,由用户交互给出。
假设物体的运动轨迹为一空间参数曲线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