一级工程实践—— Bézier 曲线算法

博客分类: 一级工程实践 阅读次数: 102 次

一级工程实践—— Bézier 曲线算法

贝塞尔曲线 ( Bézier curve ) ,又称贝兹曲线或贝济埃曲线,是应用于二维图形应用程序的数学曲线,它是依据四个位置任意的点坐标绘制出的一条光滑曲线。

Bézier 算法介绍

我们上次课学了 DDA 直线算法Bresenham 直线算法 ,虽然算法不同,但其核心思路都是根据直线的参数方程,通过控制点位置,计算直线上的其他点的坐标。确定一条直线只需要两个控制点;确定一条曲线至少需要三个控制点;而确定一条贝塞尔曲线则需要四个控制点。
下面我们将从 线性公式,到 二次方公式 再到 三次方公式,然后推导出一般性的 n 次方贝塞尔曲线公式

1. Linear Bézier curves

给定点 P0P1,线性贝塞尔曲线只是一条两点之间的直线。这条线由下式给出:

image

其等同于线性插值。

2. Quadratic Bézier curves

二次方贝塞尔曲线的路径由给定点 P0P1P2 的函数 B(t) 给出:

image

当参数 t 变化时,绘制出的曲线如下图所示:

image

3. Cubic Bézier curves

P0P1P2P3 四个点在平面或在三维空间中定义了三次方贝塞尔曲线。曲线起始于 P0 走向 P1,并从 P2 的方向来到 P3 。一般不会经过 P1P2 ;这两个点只是在那里提供方向资讯。P0P1 之间的间距,决定了曲线在转而趋进 P3 之前,走向 P2 方向的 长度有多长

image

当参数 t 变化时,绘制出的曲线如下图所示:

image

4. Higher-Order Bézier curves

由以上公式可以推断得到 n阶贝塞尔曲线公式
给定 n 个控制点 P0P1 、… 、Pn,曲线轨迹由下述公式给出:

image

(貌似跟老师PPT上的不一样是不是?没关系,整理一下就好了)

对于 n 个控制点 Pk = (xk, yk, zk),曲线上的任意一点 P(u) 坐标:

image

其中,u 是函数的参数,变化范围为 0 ~ 1

image

image

当参数 u 变化时,绘制出的曲线如下图所示:

image

五个控制点

image

六个控制点

关键代码

//计算n!/(k!(n-k)!),存储到c[k],其中k=0,1,...,n
void BezierCurve::binomiaCoeffs(int n, int *c)
{
	int k, i;

	for (k = 0; k <= n; k++)
	{
		//计算n!/(k!(n-k)!)
		c[k] = 1;
		for (i = n; i >= k + 1; i--)
			c[k] *= i;
		for (i = n - k; i >= 2; i--)
			c[k] /= i;
	}
}

//Bezier曲线中,起点为B(0),终点为B(1),该函数计算点B(u)的坐标,其中0<=u<=1;
//bezPt 指向B(u)的指针,
//nCtrlPts 控制点个数
//ctrlPts 控制点内存起始地址
void BezierCurve::computeBezPt(float u, wcPt3D *bezPt, int nCtrlPts, wcPt3D *ctrlPts, int *C) 
{
	//powf(x,k) x的k次方
	int k, n = nCtrlPts - 1;
	float bezBlendFcn;
	bezPt->x = bezPt->y = bezPt->z = 0.0;

	for (k = 0; k < nCtrlPts; k++)
	{
		bezBlendFcn = C[k] * pow(u, k)*pow(1 - u, n - k);
		bezPt->x += ctrlPts[k].x*bezBlendFcn;
		bezPt->y += ctrlPts[k].y*bezBlendFcn;
		bezPt->z += ctrlPts[k].z*bezBlendFcn;
	}
}


//计算bezier曲线
//ctrlPts 控制点数组内存起始地址
//nCtrlPts 控制点个数
//bezCurvePts 曲线点数组内存起始地址
//nBezCurvePts 曲线点个数
void BezierCurve::bezier(wcPt3D *ctrlPt3D, int nCtrlPts, int nBezCurvePts)
{
	wcPt3D BezCurvePt;
	float u; int *C, k;

	C = new int[nCtrlPts];
	binomiaCoeffs(nCtrlPts - 1, C);
	for (k = 0; k < nBezCurvePts; k++)
	{
		u = float(k) / float(nBezCurvePts);
		computeBezPt(u, &BezCurvePt, nCtrlPts, ctrlPt3D, C);
		plotPoint(BezCurvePt);
	}
	delete[]C;
}

运行结果示例

image

参考文献

【1】https://baike.baidu.com/item/%E8%B4%9D%E5%A1%9E%E5%B0%94%E6%9B%B2%E7%BA%BF/1091769?fr=aladdin