opengl绘制二次,三次,四次插值曲线

引言

因为涉及许多矩阵操作,自己写一个完善的矩阵类较为复杂,所以这里使用了c/c++环境下用的比较多的线性代数库eigen。
配置过程如下

公式的推导

插值曲线就是给出若干个控制点及一些约束条件,构造出一条曲线来经过或者逼近这几个控制点,最简单的曲线形式就是多项式曲线,依据多项式内的各项的最高次数,依次分为二次曲线,三次曲线,四次曲线…

最简单的插值曲线形式就是给出N+1个点,然后构造出一条N次曲线来依次经过这几个点,这几个点又称为控制点。这样马上就能产生一个问题,几个点如何确定一条曲线呢?至少需要多少个点才能确定一条曲线呢?以及最重要的是,为什么这样是可行的呢?很多书上往往语焉不详

以最简单的二次曲线为例(如果是一次就退化成直线了,所以最低要二次才是曲线)

二次曲线的显式表示为

在这里插入图片描述

在计算机图形学中使用显式曲线形式有很多问题,如一个x值只能对应一个y值,所以一般采用参数曲线形式。
我们这里引入t作为参数,参数曲线形式的二次曲线表示为

在这里插入图片描述

可以说abc三个系数决定一条二次曲线,对于参数曲线x(t),y(t)两个函数分别需要三个系数来确定,加起来一共是六个系数。如果要增加z轴,也只需要再增加一个z(t)即可,此时一共要是九个系数。

因此要得到一条曲线只要解出abc三个系数。

假设此时有三个点(x1,y1),(x2,y2),(x3,y3),当t分别取0,0.5,1时曲线依次经过这三个点,可以想象成t=0时是该参数曲线的起点,t=0.5是该参数曲线向前移动到一半时候的点,t=1是该参数曲线的终点。

以参数曲线的x(t)部分为例,代入这些值可以得到一个方程组

在这里插入图片描述
熟悉线性代数的马上可以看出这样明显可以转换成一个矩阵相乘的形式,上面这个方程组等价于下面这个矩阵运算。
在这里插入图片描述
左侧的矩阵称为约束矩阵。可以看出,此时要解出x(t)的三个参数,只要等式两边左乘约束矩阵的逆矩阵即可。
如下所示
在这里插入图片描述
y(t)也是同理,这样就可以解出曲线的系数,这个过程也可以很容易扩展到三次曲线。也可以很容易扩展到三维空间中的曲线,只要再加入z(t)即可。

因此给定几个点求一条曲线经过它,只是一个非常简单的解方程的问题。

思考一下就可以发现,上面这个计算过程需要对约束矩阵求逆,而只有方阵才能求逆。二次曲线有三个系数,所以约束矩阵的列数已经确定是三,为了构成一个方阵,该矩阵的行数也必须是三才行,每行代表一个点,所以必须要有三个点的数据,才能得出一条二次曲线。

得出结论,插值曲线使其经过给定控制点,二次曲线因为有三个系数,所以需要三个点确定一条二次曲线。
而N次曲线有N+1个系数,所以需要N+1个点来确定一条N次曲线

推导完成后我们开始编写代码

二次插值曲线

首先给出三个点的坐标,xdatas给出三个点的x坐标,ydatas给出三个点的y坐标。
这里使用了eigen的matrix类来构造列向量。

	//控制点的数据 为列向量
	Matrix<double,3,1> xdatas = { 100,200,400 };
	Matrix<double, 3, 1> ydatas = { 200,400,350 };
	quadraticInterpolationCurve(xdatas, ydatas);

具体函数如下

//绘制二次插值曲线
void quadraticInterpolationCurve(Matrix<double, 3, 1> xdatas, Matrix<double, 3, 1> ydatas)
{
	Matrix3d m;
	m << 0, 0, 1,
		0.25, 0.5, 1,
		1, 1, 1;

	//画出控制点
	glPointSize(5);
	glColor3f(0, 1, 0);
	glBegin(GL_POINTS);
	for (int i = 0; i < 3; i++)
		glVertex2f(xdatas[i], ydatas[i]);
	glEnd();

	//计算出二次曲线的系数
	Matrix<double, 1, 3> xabc = m.inverse() * xdatas;
	Matrix<double, 1, 3> yabc = m.inverse() * ydatas;

	//绘制出所得曲线
	glColor3f(1, 0, 0);
	glPointSize(2);
	glBegin(GL_POINTS);
	for (double t = 0; t <= 1; t += 0.01f)
	{
		double x = xabc[0] * t * t + xabc[1] * t + xabc[2];
		double y = yabc[0] * t * t + yabc[1] * t + yabc[2];
		glVertex2f(x, y);
	}
	glEnd();
	glFlush();
}

矩阵m就是我们前面推导出的约束矩阵,之后 m.inverse() * xdatas;就算出了x(t)曲线的三个系数 ,m.inverse() * ydatas;算出了y(t)的三个系数

最后用一个循环画点来画出该曲线,将t从0步进到1就画出了该曲线。
在这里插入图片描述
可以看出,该曲线经过了三个绿色的控制点,证明我们生成了一条经过三个点的二次曲线。

三次插值曲线

我们继续编写三次插值曲线代码,这时需要四个控制点,我们分别平均取t=0,t=1/3,t=2/3,t=1时的四个控制点
推导出约束矩阵为
在这里插入图片描述
编写代码

//绘制三次插值曲线
void cubicInterpolationCurve(Matrix<double, 4, 1> xdatas, Matrix<double, 4, 1> ydatas)
{
	//代入t值为0 1/3 2/3 1得出的矩阵
	Matrix4d m;
	m << 0, 0, 0, 1,
		pow(1 / 3.f, 3), pow(1 / 3.f, 2), 1 / 3.f, 1,
		pow(2 / 3.f, 3), pow(2 / 3.f, 2), 2 / 3.f, 1,
		1, 1, 1, 1;

	//画出控制点
	glPointSize(5);
	glColor3f(0, 1, 0);
	glBegin(GL_POINTS);
	for (int i = 0; i < 4; i++)
		glVertex2f(xdatas[i], ydatas[i]);
	glEnd();

	//计算出三次曲线的系数
	Matrix<double, 1, 4> xabc = m.inverse() * xdatas;
	Matrix<double, 1, 4> yabc = m.inverse() * ydatas;

	//绘制出所得曲线
	glColor3f(1, 0, 0);
	glPointSize(2);
	glBegin(GL_POINTS);
	for (double t = 0; t <= 1; t += 0.01f)
	{
		double x = xabc[0] * t * t * t + xabc[1] * t * t + xabc[2] * t + xabc[3];
		double y = yabc[0] * t * t * t + yabc[1] * t * t + yabc[2] * t + yabc[3];
		glVertex2f(x, y);
	}
	glEnd();
	glFlush();
}

在这里插入图片描述
可以看出该曲线经过四个控制点

四次插值曲线

四次曲线也是一样

//绘制四次插值曲线
void quarticInterpolationCurve(Matrix<double, 5, 1> xdatas, Matrix<double, 5, 1> ydatas)
{
	Matrix<double,5,5> m;
	m << 0, 0, 0, 0,1,
		pow(1 / 4.f, 4), pow(1 / 4.f, 3), pow(1 / 4.f, 2),1 / 4.f, 1,
		pow(2 / 4.f, 4), pow(2 / 4.f, 3), pow(2 / 4.f, 2),2 / 4.f, 1,
		pow(3 / 4.f, 4), pow(3 / 4.f, 3), pow(3 / 4.f, 2), 3 / 4.f, 1,
		1, 1, 1, 1,1;


	//画出控制点
	glPointSize(5);
	glColor3f(0, 1, 0);
	glBegin(GL_POINTS);
	for (int i = 0; i < 5; i++)
		glVertex2f(xdatas[i], ydatas[i]);
	glEnd();

	//计算出四次曲线的系数
	Matrix<double, 1, 5> xabc = m.inverse() * xdatas;
	Matrix<double, 1, 5> yabc = m.inverse() * ydatas;

	//绘制出所得曲线
	glColor3f(1, 0, 0);
	glPointSize(2);
	glBegin(GL_POINTS);
	for (double t = 0; t <= 1; t += 0.01f)
	{
		double x = xabc[0] * t * t * t*t + xabc[1] * t * t*t + xabc[2] * t*t + xabc[3]*t+xabc[4];
		double y = yabc[0] * t * t * t * t + yabc[1] * t * t * t + yabc[2] * t * t + yabc[3] * t + yabc[4];
		glVertex2f(x, y);
	}
	glEnd();
	glFlush();
}

在这里插入图片描述
可以看出该曲线依次经过五个控制点

结尾

上面的t值都在0和1之间做了平均分配,其实t值也可以任意取0-1间的任何数,分配不同的t值,也会使约束矩阵发生变化,不过平均分配下得出的曲线看起来是最自然的。

以上的求解过程都用到了约束矩阵,约束矩阵中包括了几个点的位置来构成一个方阵,在给定的约束矩阵下,可以唯一确定一条曲线。
但是约束不是只有一种‘经过某个点’,为了使给定的曲线具有别的性质,发展出了许多不同的插值曲线,有时可能希望曲线在端点处具有给定的斜率,有时也可能希望曲线只是逼近控制点而不穿过控制点。

根据不同的约束条件可以构建出不同的插值曲线,如三次hermite曲线的约束条件只要经过两个控制点,但是必须在两个控制点处具有给定的斜率,这样也是四个条件可以构成一个4*4的约束矩阵。

下一篇讲了hermite曲线和cardinal曲线的画法

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值