OpenGL绘制Bezier曲线的方法

作者:process-z.com 时间:2023-11-01 03:59:30 

本文实例为大家分享了OpenGL绘制Bezier曲线的具体代码,供大家参考,具体内容如下

项目要求:

– 使用鼠标在屏幕中任意设置控制点,并生成曲线

– 使用鼠标和键盘的交互操作实现对曲线的修改。

项目总体介绍

本项目利用Bezier曲线生成算法生成可由用户自定义的曲线。可实现核心功能如下:

1、用户用鼠标左击屏幕任意处产生记录点。

2、鼠标右击屏幕任意处由先前的任意个数记录点和其先后关系生成Bezier曲线。

另有辅助输入功能:

1、按键盘‘C'键可清除所有记录点。

2、按键盘‘R'键可清除上一个记录点。

3、按键盘‘Q'键可推出程序。

项目设计思路

1、Bezier曲线介绍:

贝塞尔曲线就是这样的一条曲线,它是依据四个位置任意的点坐标绘制出的一条光滑曲线。在历史上,研究贝塞尔曲线的人最初是按照已知曲线参数方程来确定四个点的思路设计出这种矢量曲线绘制法。1962年,法国数学家Pierre Bézier第一个研究了这种矢量绘制曲线的方法,并给出了详细的计算公式,因此按照这样的公式绘制出来的曲线就用他的姓氏来命名是为贝塞尔曲线。

2、生成公式:

(1)线性公式(只有两个点情况)

给定点P0、P1,线性贝兹曲线只是一条两点之间的直线。这条线由下式给出:

且其等同于线性插值。

(2)二次方公式(三个点组成)

二次方贝兹曲线的路径由给定点P0、P1、P2的函数B(t)追踪:

TrueType字型就运用了以贝兹样条组成的二次贝兹曲线。

(3)三次方公式(四个点)

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

曲线的参数形式为:

现代的成象系统,如PostScript、Asymptote和Metafont,运用了以贝兹样条组成的三次贝兹曲线,用来描绘曲线轮廓。

(4)一般参数公式(n个点)

阶贝兹曲线可如下推断。给定点P0、P1、…、Pn,其贝兹曲线即:

N阶的贝兹曲线,即N-1阶贝兹曲线之间的插值。


#include<stdlib.h>
#include<stdio.h>
#include<math.h>
#include<GL/glut.h>
//定义控制点数目的最大值
#define MAX_CPTX 25
int ncpts=0;//实际控制点个数
static int width=600,height=600;//窗口大小
typedef struct
{
GLfloat x,y;
} POINT;
POINT cpts[MAX_CPTX];//存储控制点坐标
//求n!
int JieCheng(int n)
{
if(n==1||n==0)
{
return 1;
}
else
{
return n*JieCheng(n-1);
}
}
//求组合排列
double C(int n,int i)
{
return ((double)JieCheng(n))/((double)(JieCheng(i)*JieCheng(n-i)));
}
//求一个数u的num次方
double N(double u,int n)
{
double sum=1.0;
if (n==0)
{
return 1;
}
for(int i=0;i<n;i++)
{
sum*=u;
}
return sum;
}

//绘制bezier曲线
void drawBezier(POINT *p)
{
void display();
if(ncpts<=0) return;

POINT *p1;
p1=new POINT[1000];
GLfloat u=0,x,y;
int i,num=1;
p1[0]=p[0];
for(u=0;u<=1;u=u+0.001)
{
x=0;
y=0;
for(i=0;i<ncpts;i++)
{
 x+=C(ncpts-1,i)*N(u,i)*N((1-u),(ncpts-1-i))*p[i].x;
 y+=C(ncpts-1,i)*N(u,i)*N((1-u),(ncpts-1-i))*p[i].y;
}
p1[num].x=x;
p1[num].y=y;
num++;
}

glPointSize(4.0);
 glColor3f(0.0,0.0,0.0);
 glBegin(GL_LINE_STRIP);
 for(int k=0;k<1000;k++)
glVertex2f(p1[k].x,p1[k].y);
 glEnd();
 glFlush();
return;
}

//输入新的控制点
static void mouse(int button, int state,int x,int y)
{
void display();
float wx,wy;
//鼠标未按下左键,不做响应
if(state!=GLUT_DOWN)
return;
else
{if(button==GLUT_LEFT_BUTTON)
{
//转换坐标
wx=(2.0*x)/(float)(width-1)-1.0;
wy=(2.0*(height-1-y))/(float)(height-1)-1.0;
//判断控制点数目是否超过最大值
if(ncpts==MAX_CPTX)
return;
//存储控制点
cpts[ncpts].x=wx;
cpts[ncpts].y=wy;
ncpts++;
//绘制控制点
glColor3f(0.0,0.0,0.0);
glPointSize(5.0);
glBegin(GL_POINTS);
glVertex2f(wx,wy);
glEnd();
glFlush();
}
if(button==GLUT_RIGHT_BUTTON)
{
display();
drawBezier(cpts);
}
}
}
void display(void)
{
int i;
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(0.0,0.0,0.0);
glPointSize(5.0);
glBegin(GL_POINTS);
for (i = 0; i < ncpts; i++)
 glVertex2f(cpts[i].x,cpts[i].y);
glEnd();
glFlush();

}
//键盘回调函数
void keyboard(unsigned char key,int x,int y)
{
switch (key)
{
 case 'q': case 'Q':
  exit(0);
  break;
 case 'c': case 'C':
ncpts = 0;
glutPostRedisplay();
  break;
case 'r': case 'R':
ncpts--;
glutPostRedisplay();
break;
}
}

//重绘函数
void reshape(int w,int h)
{
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-1.0,1.0,-1.0,1.0,-1.0,1.0);
glMatrixMode(GL_MODELVIEW);
glViewport(0,0,w,h);//调整视口
width=w;
height=h;
}
int main(int argc, char **argv)
{
//初始化
glutInit(&argc,argv);
glutInitDisplayMode(GLUT_RGB);
glutInitWindowSize(width,height);
glutCreateWindow("zjc2012211763");
//注册回调函数
glutDisplayFunc(display);
glutMouseFunc(mouse);
glutKeyboardFunc(keyboard);
glutReshapeFunc(reshape);
glClearColor(1.0,1.0,1.0,1.0);
glColor3f(0.0,0.0,0.0);
glutMainLoop();
}

来源:https://blog.csdn.net/zjccoder/article/details/42807645

标签:OpenGL,Bezier,曲线
0
投稿

猜你喜欢

  • android在异步任务中关闭Cursor的代码方法

    2022-04-21 18:07:32
  • Spring Boot多个定时任务阻塞问题的解决方法

    2023-09-20 11:43:36
  • C#中重载与重写区别分析

    2023-11-01 17:43:05
  • java中的Object类的toSpring()方法

    2022-08-30 12:36:03
  • Springboot实现图片上传功能的示例代码

    2022-08-05 22:46:44
  • SpringMVC实现数据绑定及表单标签

    2022-03-24 18:06:47
  • Java使用过滤器防止SQL注入XSS脚本注入的实现

    2021-09-14 18:17:19
  • 详解Java8 CompletableFuture的并行处理用法

    2023-07-27 11:48:06
  • Android定时器和倒计时实现淘宝秒杀功能

    2023-01-18 02:17:04
  • java8中Stream的使用以及分割list案例

    2022-08-09 10:08:48
  • Logger.error打印错误异常的详细堆栈信息

    2022-01-06 23:03:04
  • SpringCloud实现Eureka服务注册与发现

    2021-08-16 04:00:44
  • Java Lambda List转Map代码实例

    2022-05-24 20:15:42
  • WPF实现3D翻牌式倒计时特效

    2021-11-11 16:08:21
  • gateway网关与前端请求跨域问题的解决方案

    2022-09-20 01:30:44
  • c# 根据NPOI 读取一个excel 文件的多个Sheet

    2021-11-30 05:10:55
  • 浅析C# 委托(Delegate)

    2023-01-26 20:11:45
  • c#入门之分支语句使用方法(三元运算符、if语句、switch语句)

    2021-12-06 00:55:20
  • 在WPF中实现平滑滚动的方法详解

    2021-06-17 06:34:16
  • C#处理Paint事件的方法

    2022-01-12 02:02:51
  • asp之家 软件编程 m.aspxhome.com