定义实现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]]
B(t)=i=0∑nPi∁niti(1−t)n−i,其中[tϵ(0,1]]
不要被这么一坨公式给吓到,它求的其实是
t
t
t为定值
时贝塞尔曲线上的点的坐标,而
P
i
P_i
Pi则表示第
i
i
i个控制点的作用.不同的
t
t
t值可以画出不同的点,这些点就连成了贝塞尔曲线.
一阶贝塞尔曲线
B
(
t
)
=
(
1
−
t
)
P
0
+
t
P
1
B(t) = (1-t)P_0 +tP_1
B(t)=(1−t)P0+tP1
二阶贝塞尔曲线
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)=(1−t)2P0+2t(1−t)P1+t2P2
三阶贝塞尔曲线
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}
B(t)=i=0∑3Pi∁3iti(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)
Pi∗=n+1iPi−1+(1−n+1i)Pi,(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}
Pik=={Pi(1−t)Pik−1+tPi+1k−1k=0x!=0
代码实现
#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();
}