图形学之贝塞尔曲线实现

定义实现bezier曲线

定义

B ( t ) = i = 0 n P i n i t i ( 1 t ) n i , [ t ϵ ( 0 , 1 ] ] B(t)=\sum_{i=0}^n P_i \complement_n^i t^i (1-t)^{n-i} ,其中[t\epsilon (0,1]]
不要被这么一坨公式给吓到,它求的其实是 t t 定值时贝塞尔曲线上的点的坐标,而 P i P_i 则表示第 i i 个控制点的作用.不同的 t t 值可以画出不同的点,这些点就连成了贝塞尔曲线.

一阶贝塞尔曲线

B ( t ) = ( 1 t ) P 0 + t P 1 B(t) = (1-t)P_0 +tP_1
在这里插入图片描述

二阶贝塞尔曲线

B ( t ) = ( 1 t ) 2 P 0 + 2 t ( 1 t ) P 1 + t 2 P 2 B(t)=(1-t)^2P_0 + 2t(1-t)P_1+t^2P_2
在这里插入图片描述

三阶贝塞尔曲线

B ( t ) = i = 0 3 P i 3 i t i ( 1 t ) n i B(t)=\sum_{i=0}^3 P_i \complement_3^i t^i (1-t)^{n-i}
在这里插入图片描述

贝塞尔曲线升阶

升阶意思是增加控制点,但保持原曲线不变
P i = i n + 1 P i 1 + ( 1 i n + 1 ) P i , ( i = 0 , 1 , 2... , n + 1 ) P_i^*= \frac{i}{n+1}P_{i-1}+(1-\frac{i}{n+1})P_i ,(i=0,1,2...,n+1)

代码实现

#include <GL/glut.h>
#include <stdlib.h>
#include <math.h>

GLfloat xwcMin = -250.0, xwcMax = 250.0;
GLfloat ywcMin = -250.0, ywcMax = 250.0;

class WcPt3d {
public:
	GLfloat x, y, z;
};

// 画点
void plotPt(WcPt3d point) {
	glBegin(GL_POINTS);
	glVertex3f(point.x,point.y,point.z);
	glEnd();
}

// 初始化背景色
void init(void) {
	glClearColor(1.0, 1.0, 1.0, 0.0);
}


// 计算二项式的系数 ***
void BinomialCoeffi(GLint *C, GLint ctrlNums) {
	GLint k, j;
	for ( k = 0; k <= ctrlNums; k++)
	{
		C[k] = 1;
		// n!/(k!(n-k)!)
		for ( j = ctrlNums; j >= k+1 ; j--) // n!/k!
		{
			C[k] *= j;
		}

		for (j = ctrlNums - k; j >= 2; j--) // (n-k)!
		{
			C[k] /= j;
		}
	}
}

void computeBezierPts(GLfloat t, WcPt3d * bezierCurvePt,GLint ctrlNums,GLint* C,WcPt3d* ctrlPts) {
	GLint k, n = ctrlNums - 1;
	GLfloat bezierCurveFcn;

	bezierCurvePt->x = bezierCurvePt->y = bezierCurvePt->z = 0;
	for ( k = 0; k < ctrlNums; k++)
	{
		bezierCurveFcn = C[k] * pow(t, k) * pow(1 - t, n - k);
		bezierCurvePt->x += ctrlPts[k].x * bezierCurveFcn;
		bezierCurvePt->y += ctrlPts[k].y * bezierCurveFcn;
		bezierCurvePt->z += ctrlPts[k].z * bezierCurveFcn;
	}
}
// 绘制连接线段
void plotLines(WcPt3d* points, GLint Ptnums) {
	glBegin(GL_LINE_STRIP);
	for (GLint i = 0; i < Ptnums; i++)
	{
		glVertex3f(points[i].x, points[i].y, points[i].z);
	}
	glEnd();
}
// Bezier曲线的算法
void Bezier(WcPt3d *ctrlPts, GLint bezierCurveNums, GLint ctrlNums){
	WcPt3d bezierCurvePt;
	GLint k, *C; // 获取控制点的数目 
	GLfloat t;
	C = new GLint[ctrlNums]; // 获取多项式的各项系数
	
	BinomialCoeffi(C, ctrlNums - 1); // 计算各项的系数,并存储

	for ( k = 0; k <= bezierCurveNums; k++)
	{
		t = (GLfloat)k / (GLfloat)bezierCurveNums;
		computeBezierPts(t, &bezierCurvePt, ctrlNums,C,ctrlPts);
		plotPt(bezierCurvePt);
	}
	// 绘制线段
	plotLines(ctrlPts, ctrlNums);

	delete[] C;
}
// 贝塞尔曲线的升阶
void ascendingBezier(WcPt3d* oldPts,GLint oldPtnums, GLint bezierCurveNums) {
	GLint newPtNums = oldPtnums + 1;
	WcPt3d* newPts = new WcPt3d[newPtNums];
	GLint i;

	// P[i]' = i/(n+1) P[i-1] + (n+1-i)/(n+1) Pi;
	for (i = 0; i < newPtNums; i++)
	{
		GLfloat x = 0, y = 0, z= 0;
		if (i==0)
		{
			x = oldPts[i].x;
			y = oldPts[i].y;
			z = oldPts[i].z;
		}
		else if(i==newPtNums - 1){
			x = oldPts[i - 1].x;
			y = oldPts[i - 1].y;
			z = oldPts[i - 1].z;
		}
		else {
			x = (GLfloat) oldPts[i - 1].x * i / oldPtnums + oldPts[i].x * (oldPtnums - i) / oldPtnums;
			y = (GLfloat)oldPts[i - 1].y * i / oldPtnums + oldPts[i].y * (oldPtnums - i) / oldPtnums;
			z = (GLfloat)oldPts[i - 1].z * i / oldPtnums + oldPts[i].z * (oldPtnums - i) / oldPtnums;
		}
		newPts[i] = { x,y,z };
	}
	Bezier(newPts, bezierCurveNums, newPtNums);
}


// 初始化数据并进行绘制
void displayFcn1(void) {
	WcPt3d ctrlPts[7] = { {-140,-40,0}, {-60,-80,0},{90,100,0},{120,200,0},{180,160,0},{200,130,0},{230,10,0}};
	
	GLint bezierCurveNums = 2000 , ctrlNums = 7;
	glClear(GL_COLOR_BUFFER_BIT);
	glPointSize(2);
	glColor3f(0, 0, 0);
	// 将控制点,绘制精度代入
	Bezier(ctrlPts,bezierCurveNums,ctrlNums);
	
	glColor3f(1, 1, 0);
	// 升阶
	ascendingBezier(ctrlPts, ctrlNums,bezierCurveNums);

	glFlush();

}

// 对绘图窗口进行缩放
void winReshapeFcn(GLint newWidth, GLint newHeight) {
	glViewport(0, 0, newHeight, newHeight);

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	gluOrtho2D(xwcMin, xwcMax, ywcMin, ywcMax);
	glClear(GL_COLOR_BUFFER_BIT);

}

// 主函数
int main(int argc, char **argv){
	glutInit(&argc, argv);

	glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
	glutInitWindowPosition(50, 50);
	glutInitWindowSize(600, 600);
	glutCreateWindow("贝塞尔曲线");

	init();

	glutDisplayFunc(displayFcn1);
	glutReshapeFunc(winReshapeFcn);
	glutMainLoop();
}

效果图

在这里插入图片描述

通过de Casteljau

de Casteljau递推法

由于通过贝塞尔方程计算太过复杂,所以提出了递推法,方程如下
P i k = = { P i k=0 ( 1 t ) P i k 1 + t P i + 1 k 1 x!=0 P_i^k= =\begin{cases} P_i&\text{k=0}\\(1-t)P_i^{k-1}+tP_{i+1}^{k-1}&\text{x!=0} \end{cases}
在这里插入图片描述
在这里插入图片描述

代码实现

#include <GL/glut.h>
#include <math.h>
#include <stdlib.h>

class WcPt3D
{
public:
	GLfloat x, y, z;
};

GLfloat xwcMin = -250.0, xwcMax = 250.0;
GLfloat ywcMin = -250.0, ywcMax = 250.0;

void plotPt(WcPt3D pt) {
	glBegin(GL_POINTS);
	glVertex3f(pt.x, pt.y, pt.z);
	glEnd();
}
// 当为1阶时
void oneCasteljau(WcPt3D* points, GLfloat t) {
	WcPt3D point;
	point.x = points[0].x * (1 - t) + points[1].x * t;
	point.y = points[0].y * (1 - t) + points[1].y * t;
	point.z = points[0].z * (1 - t) + points[1].z * t;
	plotPt(point);
}
// 当为n阶时
void nCasteljau(WcPt3D* Points, GLint n, GLfloat t) { // 绘制的点 , 阶数 = 点数 -1, t 目前的比值

	if (n > 1) 
	{
		WcPt3D* newCtrls = new WcPt3D[n]; // 获取n-1阶的点 
		GLint k;
		for ( k = 0; k < n; k++)
		{
			newCtrls[k].x = Points[k].x * (1 - t) + Points[k + 1].x * t;
			newCtrls[k].y = Points[k].y * (1 - t) + Points[k + 1].y * t;
			newCtrls[k].z = Points[k].z * (1 - t) + Points[k + 1].z * t;
		}
		nCasteljau(newCtrls, n - 1, t);
	}
	else {
		oneCasteljau(Points,t);
	}
}

// 绘制连接线段
void plotLines(WcPt3D* points, GLint Ptnums) {
	glBegin(GL_LINE_STRIP);
	for (GLint i = 0; i < Ptnums; i++)
	{
		glVertex3f(points[i].x, points[i].y, points[i].z);
	}
	glEnd();
}
// 使用算法绘制曲线
void deCasteljau(WcPt3D* ctrlPts, GLint ctrlNums, GLint bezierCurveNums) {

	GLint k;

	for ( k = 0; k <bezierCurveNums; k++)
	{
		GLfloat t = (GLfloat)k / (GLfloat)bezierCurveNums;
		nCasteljau(ctrlPts, ctrlNums - 1, t);
	}

	// 绘制线段
	plotLines(ctrlPts, ctrlNums);
}

void displayFcn() {

	WcPt3D ctrlPts[7] = { {-140,-40,0}, {-60,-80,0},{90,100,0},{120,200,0},{180,160,0},{200,130,0},{230,10,0} };

	GLint bezierCurveNums = 2000, ctrlNums = 7;

	glClear(GL_COLOR_BUFFER_BIT);
	glPointSize(2);
	glColor3f(0, 0, 0);
	
	deCasteljau(ctrlPts, ctrlNums, bezierCurveNums);
	glFlush();

}

// 对绘图窗口进行缩放
void winReshapeFcn(GLint newWidth, GLint newHeight) {
	glViewport(0, 0, newHeight, newHeight);

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	gluOrtho2D(xwcMin, xwcMax, ywcMin, ywcMax);
	glClear(GL_COLOR_BUFFER_BIT);

}

void init() {
	glClearColor(1.0, 1.0, 1.0, 0.0);
}
int main( int argc,  char **argv) {
	glutInit(&argc, argv);

	glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
	glutInitWindowPosition(50, 50);
	glutInitWindowSize(600, 600);
	glutCreateWindow("deCasteljau使用");
	init();
	glutDisplayFunc(displayFcn);
	glutReshapeFunc(winReshapeFcn);
	glutMainLoop();
}

效果图

在这里插入图片描述

代码地址.

猜你喜欢

转载自blog.csdn.net/qq_40552152/article/details/106065522
今日推荐