贝塞尔曲线

关于贝塞尔曲线,网上很多博客都已经给出了解释。。。真的好多。

但是我看了几百遍都不明白!!不知道大家跟我有没有同样的感受。所以就来个重点解释,通俗易懂版给大家吧~~

下面的这个图,相信你也看到过很多。然而,我这里也是需要贴一下这个图的(不知道是哪个大神的图,不好意思,借用一下)。

 

参数讲解

P0是曲线的开始点

P3是曲线的结束点

P1和P2是控制曲线走势的控制点,所以这两个点事实上是辅助作用,并不会在画布中被绘制出来

 

t参数重点讲解

t是辅助参数,可以看到它的值范围是[0,1]。这个t值作用于图中的所有直线(P0P1、P1P2、P2P3、两条绿线、蓝线)。

注意:图中真实绘制出来的,就只有红线,其他的都只是辅助的,并不会被真实绘制出来。

 

在上图中,你可以看成这个三次贝塞尔曲线由两个二次贝塞尔曲线组成。1.P0P1P2组成的二次贝塞尔曲线     2.P1P2P3组成的二次贝塞尔曲线。

所以要把上图的三次贝塞尔曲线拆分成两个二次贝塞尔曲线讲解

 

例如:

对于第一个二次贝塞尔曲线P0P1P2,

当t=0.5的时候(其实就是[0,1]的中间值,这个比较好理解),情况应该是这样的:

1.找到P0P1线(方向P0->P1,这是有方向的线段)的50% (因为t=0.5,即0.5*100%) 的位置,标上一个绿色的点

2.同1步骤,在P1P2线上的50%位置上标上一个绿色的点

3.把步骤1和2的绿色点连成一条线

4.然后在这条绿色的50%位置处标上一个红点,这个红点就是实际绘制的曲线中的一个点。(当t值不断变化,就会出现不同位置的红点,组成一条曲线)

 

同理,

第二个二次贝塞尔曲线的理解跟第一个二次贝塞尔曲线一样


------------------------  我不是分割线  ------------------------------------

注意:

       1、双三次贝塞尔曲线的4条边界都是三次贝塞尔曲线,其特征网络有16个顶点。

       2、opengl中的赛贝尔曲线:

        

求值器

OpenGL提供了一些函数来绘制贝塞尔曲线和曲面。我们只需要提供控制点和u,v作为参数,然后调用求值函数来绘制曲线。

2D曲线的例子:

//控制点 GLint numOfPoints = 4; static GLfloat controlPoints[4][3] = {{-4.0f, 0.0f, 0.0f},
{-6.0f4.0f0.0f},
{6.0f-4.0f0.0f},
{4.0f0.0f0.0f}}; void SetupRC()
{
  glClearColor(0.0f0.0f0.0f1.0f);
  glColor3f(1.0f0.0f1.0f);
} 
//画控制点
void DrawPoints()
{
  glPointSize(2.5f);
  glBegin(GL_POINTS); for (int i = 0; i < numOfPoints; ++i)
    {
      glVertex3fv(controlPoints[i]);
    }
  glEnd();
} 

void ChangeSize(GLsizei w, GLsizei h)
{
  if (h == 0)
  {
    h = 1;
  }

  glViewport(00, w, h);
   //使用正交投影
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();

  gluOrtho2D(-10.0f10.0f-10.0f10.0f);

  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
} 
void RenderScene()
{
  glClear(GL_COLOR_BUFFER_BIT);
   //设置贝塞尔曲线,这个函数其实只需要调用一次,可以放在SetupRC中设置
    glMap1f(GL_MAP1_VERTEX_3, //生成的数据类型
     0.0f//u值的下界
      100.0f//u值的上界
       3//顶点在数据中的间隔,x,y,z所以间隔是3
        numOfPoints, //u方向上的阶,即控制点的个数
         &controlPoints[0][0//指向控制点数据的指针 );
   //必须在绘制顶点之前开启
   glEnable(GL_MAP1_VERTEX_3);
    //使用画线的方式来连接点
   glBegin(GL_LINE_STRIP);
  for (int i = 0; i <= 100; i++)
  {
    glEvalCoord1f((GLfloat)i);
  }
  glEnd();
  DrawPoints();
  glutSwapBuffers();
}

image

在RenderScene函数中调用glMap1f来为曲线创建映射。第一个参数为GL_MAP1_VERTEX3,设置求值器产生顶点为三元组(x,y,z).还可以设置为产生纹理坐标和颜色信息。参考glMap1.后面的两个参数设定了u的取值范围[0,100],第四个参数指定了顶点在数组中的间隔,由于顶点是由3个浮点数组成,所以间隔是3.第五个参数指定了控制点的个数,最后一个参数是控制点数组。然后我们需要启用求值器,调用如下:

glEnable(GL_MAP1_VERTEX3);

glEvalCoord1f函数,接受一个参数为曲线的参数值。调用这个函数会通过求值函数求出顶点坐标值,然后内部调用了glVertex。这里使用连线的方式来连接这些顶点:

glBegin(GL_LINE_STRIP);

for(i = 0; I <= 100; i++)

{

  glEvalCoord1f((GLfloat)i);

}

glEnd();

计算曲线

OpenGl还提供了更简单的方式来完成上面的任务。我们可以通过glMapGrid函数来设置一个网格,来告诉OpenGL在u的值域的范围内创建一个包含各个点的空间对称的网格。然后,我们调用glEvalMesh,使用指定的图元(GL_LINE或GL_POINTS)来链接各个点。

我们用下面的两个函数调用

  glMapGrid1f(1000.0f, 100.0f);
  glEvalMesh1(GL_LINE0100);

可以替换下面的代码

glBegin(GL_LINE_STRIP)
for (int i = 0; i <= 100; i++)
  {
    glEvalCoord1f((GLfloat)i);
  }
glEnd();

使用这种方式更为紧凑。

3D表面

创建一个贝塞尔曲面与创建一个贝塞尔曲线类似。除了给出u的定义域之外,还要给出v的定义域。下面的例子是创建一个贝塞尔曲面。与之前不同的是,我们沿着v的定义域定义了3组控制点。为了保持曲面的简单,这几组控制点只是z值不同。用这种方式画的曲面,看起来像是曲线沿z轴的扩展。

//控制点  GLint nNumPoints = 3;

GLfloat ctrlPoints[3][3][3]= {{{  -4.0f0.0f4.0f},    
{ -2.0f4.0f4.0f},    
{  4.0f0.0f4.0f }},

{{  -4.0f0.0f0.0f},    
{ -2.0f4.0f0.0f},    
{  4.0f0.0f0.0f }},

{{  -4.0f0.0f-4.0f},    
{ -2.0f4.0f-4.0f},    
{  4.0f0.0f-4.0f }}}; //画控制点  void DrawPoints(void)int i,j;    

  glColor3f(1.0f0.0f0.0f); //把点放大一点,看得更清楚  glPointSize(5.0f);

  glBegin(GL_POINTS); 
  for(i = 0; i < nNumPoints; i++)
   for(j = 0; j < 3; j++)
      glVertex3fv(ctrlPoints[i][j]);
  glEnd();
} 
void RenderScene(void)
{ 
// Clear the window with current clearing color 
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
   // 保存模型视图矩阵  
   glMatrixMode(GL_MODELVIEW);
  glPushMatrix(); 
  //旋转一定的角度方便观察  
  glRotatef(45.0f0.0f1.0f0.0f);
  glRotatef(60.0f1.0f0.0f0.0f);


  glColor3f(0.0f0.0f1.0f); //设置映射方式,只需要设置一次可以在SetupRC中调用。  
  glMap2f(GL_MAP2_VERTEX_3, //生成的数据类型  
  0.0f// u的下界 
  10.0f//u的上界  
  3//数据中点的间隔  
  3//u方向上的阶  
  0.0f//v的下界  
  10.0f//v的上界  
  9// 控制点之间的间隔  
  3// v方向上的阶  
  &ctrlPoints[0][0][0]); //控制点数组 
  //启用求值器  
  glEnable(GL_MAP2_VERTEX_3); 
  //从0到10映射一个包含10个点的网格  
  glMapGrid2f(10,0.0f,10.0f,10,0.0f,10.0f); 
  // 计算网格  
  glEvalMesh2(GL_LINE,0,10,0,10); 
  //画控制点  
  DrawPoints();
  
  glPopMatrix();

  glutSwapBuffers();
}

在这里我们用glMap2f替换了之前的glMap1f, 这个函数指定了u和v两个域上的点。除了指定u的上界和下界之外,还要指定v的上界和下界。v定义域内点的距离是9,因为这里使用了3维数组,包含了3个u值,每个u值又包含了3个点,3x3=9。然后指定v方向上的阶,即每个u分支上v方向有多少个点。最后一个参数是指向控制点的指针。

然后我们设置求值器.

//启用求值器

glEnable(GL_MAP2_VERTEX_3);
//从0到10映射一个包含10个点的网格

glMapGrid2f(10,0.0f,10.0f,10,0.0f,10.0f);

计算网格网格表面,用线的方式表示。

// 计算网格
  glEvalMesh2(GL_LINE,0,10,0,10);

image

光照和法线

求值器还可以帮我们生成表面的法线,只需简单的修改一些代码:

把glEvalMesh2(GL_LINE, 0, 10, 0, 10);替换为glEvalMesh2(GL_FILL, 0, 10, 0, 10);然后在初始化时 SetupRC中调用glEnable(GL_AUTO_NORMAL);就可以得到一个收到光照的曲面了。

image

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值