1. 程式人生 > 程式設計 >OpenGL繪製Bezier曲線的方法

OpenGL繪製Bezier曲線的方法

本文例項為大家分享了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,(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);
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);
 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 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);
glMatrixMode(GL_MODELVIEW);
glViewport(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);
glColor3f(0.0,0.0);
glutMainLoop();
}

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。