65.9K
CodeProject 正在变化。 阅读更多。
Home

用于相机动画的 Overhauser(Catmull-Rom)样条

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.85/5 (24投票s)

2008 年 11 月 10 日

CPOL

5分钟阅读

viewsIcon

116861

downloadIcon

3739

从游戏开发者的角度介绍 Overhauser 样条曲线,附带 C++ 示例代码

引言

许多人对游戏中逼真的相机动画或多媒体演示印象深刻。通常被称为相机插值的数学原理其实相当简单。在本文中,我将重点介绍一种简单的算法,该算法使用一类称为OverhauserCatmull-Rom的样条曲线,并展示它们如何以及为何优于其他类似的方法。

数学是你的朋友

你可能会因此讨厌我,但数学确实很美妙。在本节中,我们将回顾矢量微积分的知识,这将帮助我们更好地理解示例代码。

让我们从基础开始:一条经过其控制点的曲线被称为插值这些点。贝塞尔曲线仅插值其 4 个控制点中的 2 个,而 B 样条曲线则不插值指定的任何控制点(曲线围绕这些点平滑地通过)。Catmull-Rom 样条曲线,也称为 Overhauser 样条曲线,属于一类称为Hermite 样条的曲线。它们是均匀有理三次多项式曲线,可以在 N 个控制点之间进行插值,并且正好通过 N-2 个控制点(除了第一个和最后一个)。它们是均匀的,因为控制点(也称为节点)在曲线参数 (t) 方面以相等的间隔间隔开。插值以分段方式进行:在每对点之间定义一个新的三次曲线。

Catmull-Rom 样条曲线的参数方程由下式给出

图 1:Catmull-Rom 样条曲线的参数方程。

其中矢量 V 和 T 以及矩阵 M 为

图 2:Catmull-Rom 样条曲线方程的成员。

我们可以直接使用此方程,并使用矢量和矩阵乘法来编写我们的解决方案。虽然可行,但这可能效率不高。让我们稍微简化一下方程。我鼓励您仔细检查我的数学 - 这很有趣。通过将水平矢量 T 与矩阵 M 相乘,并考虑垂直矢量,我们得到

图 3A:Eq1 的简化。

其中 b1...b4t 的三次多项式

图 3:Eq1 的简化。

图 3A 显示了最终方程的成员。P1...P4 是控制点。在 3D 中,Pn 是齐次或非齐次矢量(3 或 4 个坐标)。在 2D 中,它们是 2 坐标矢量。

这一切到底是什么意思?嗯,它的意思是,如果您知道 N 个中间位置以及相机在 N 个时刻的角度/轴对,您就可以通过使用上述 Eq 3A 在 N-2 个位置和角度/轴对之间进行插值来生成准确平滑的相机动画。相机将通过所有中间的 N-2 个点。[注意:如果您将起始点和结束点加倍,相机将通过所有 N 个位置。]

编码实现

首先,我们需要一个类来为我们的控制点 Pn 提供抽象。我们将编写一个最小的 3D 矢量类,只包含几个操作。您可以根据需要扩展它。另请注意,随附的示例应用程序将这些矢量的 Z 坐标置零,并主要将其用于绘制 2D 曲线。但是,该包完全能够计算 3D 样条曲线!让我们回顾一下 3D 矢量类 vec3

/// Minimal 3-dimensional vector abstraction
class vec3
{
public:

    // Constructors
    vec3() : x(0), y(0), z(0)
	{}

	vec3(float vx, float vy, float vz)
	{
		x = vx;
		y = vy;
		z = vz;
	}

	vec3(const vec3& v)
	{
		x = v.x;
		y = v.y;
		z = v.z;
	}

	// Destructor
	~vec3() {}

	// A minimal set of vector operations
	vec3 operator * (float mult) const // result = this * arg
	{
		return vec3(x * mult, y * mult, z * mult);
	}

	vec3 operator + (const vec3& v) const // result = this + arg
	{
		return vec3(x + v.x, y + v.y, z + v.z);
	}

	vec3 operator - (const vec3& v) const // result = this - arg
	{
		return vec3(x - v.x, y - v.y, z - v.z);
	}

	float x, y, z;
};

相当简单。现在我们将引入一个新类来抽象我们的样条曲线。

#include "vec3.hpp"
#include 

class CRSpline
{
public:

    // Constructors and destructor
    CRSpline();
    CRSpline(const CRSpline&);
    ~CRSpline();

    // Operations
    void AddSplinePoint(const vec3& v);
    vec3 GetInterpolatedSplinePoint(float t);   // t = 0...1; 0=vp[0] ... 1=vp[max]
    int GetNumPoints();
    vec3& GetNthPoint(int n);

    // Static method for computing the Catmull-Rom parametric equation
    // given a time (t) and a vector quadruple (p1,p2,p3,p4).
    static vec3 Eq(float t, const vec3& p1, const vec3& p2, 
		const vec3& p3, const vec3& p4);

private:
    std::vector<vec3> vp;
    float delta_t;
};

这同样相当直观:CRSpline 类本质上是多个控制点(表示为 std::vector)的容器。它有一个 static 成员函数,用于为给定的参数 t 和四个控制点 P1...P4 求解样条方程。该函数返回一个 3 坐标矢量,这是给定 4 个控制点之间,给定 t 值的插值结果。

AddSplinePointGetInterpolatedSplinePoint 方法允许我们指定 2D/3D 曲线的控制点并获取平滑曲线。让我们快速看一下后者,它包含最后一点棘手的逻辑

vec3 CRSpline::GetInterpolatedSplinePoint(float t)
{
    // Find out in which interval we are on the spline
    int p = (int)(t / delta_t);
    // Compute local control point indices
#define BOUNDS(pp) { if (pp < 0) pp = 0; 
	else if (pp >= (int)vp.size()-1) pp = vp.size() - 1; }
    int p0 = p - 1;     BOUNDS(p0);
    int p1 = p;         BOUNDS(p1);
    int p2 = p + 1;     BOUNDS(p2);
    int p3 = p + 2;     BOUNDS(p3);
    // Relative (local) time
    float lt = (t - delta_t*(float)p) / delta_t;
    // Interpolate
    return CRSpline::Eq(lt, vp[p0], vp[p1], vp[p2], vp[p3]);
}

如上面的代码所示,GetInterpolatedSplinePoint 函数将样条曲线分成 4 点段,相对于局部段转换参数 t,然后使用静态方程求解器获取最终结果。该函数假定 t 的范围从 0 1,其中 0 代表样条曲线的“开始”(第一个控制点),1 代表样条曲线的“结束”(最后一个控制点)。如果这不合适,您可以自己制定时间方案;但请记住调整上面 plt 的计算。

任何 2D 或 3D 矢量序列都可以以类似的方式进行插值。例如,相机的角度/轴,场景中各种移动对象的 the positions and orientation 等。与使用 b 样条曲线或贝塞尔曲线相比,这种方法的优点是生成的曲线触及所有的控制点(再次,请确保加倍第一个和最后一个控制点以实现此目的)。

Using the Code

在这里,我包含了一个使用该库的非常原始的应用程序,用 Borland Dev Studio 4 实现。它基本上实例化了 CRSpline 类并用伪随机控制点填充它,然后使用 BDS 的 TCanvas 接口在常规对话框的画布上绘制样条曲线。

希望我这段简短的数学讲解没有让您感到无聊。请享用代码。

参考文献

  • 计算机图形学:原理与实践;Foley, van Dam, Feiner, Hughes;Addison-Wesley, 1997
  • Catmull, E. 和 R. Rom,“一类局部插值样条曲线”;计算机辅助几何设计;Academic Press, San Francisco, 1974

历史

  • 2008年11月7日:初次发布
© . All rights reserved.