歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

OpenGL超級寶典學習筆記——貝塞爾曲線和曲面

參數方程表現形式

在中學的時候,我們都學習過直線的參數方程:y = kx + b;其中k表示斜率,b表示截距(即與y軸的交點坐標)。類似地,我們也可以用一個參數方程來表示一條曲線。1962年,法國工程師貝塞爾發明了貝塞爾曲線方程。關於貝塞爾曲線的詳細介紹可以參考(維基貝塞爾)。這裡只介紹OpenGL實現貝塞爾的函數。

OpenGl定義一條曲線時,也把它定義為一個曲線方程。我們把這條曲線的參數成為u,它的值域就是曲線的定義域。曲面則需要u和v兩個參數來描述。注意,u和v參數只表示了描述曲線的參數方程的范圍,它們並沒有反映實際的坐標值。其坐標可以表示為:

x = f(u); y = g(u); z = h(u);

如下圖:

控制點

貝塞爾曲線的形狀由控制點來控制。貝塞爾曲線的控制點個數為曲線的階。根據控制點的個數,貝塞爾曲線又分為二次貝塞爾曲線,三次貝塞爾曲線,高階貝塞爾曲線。

線性曲線

線性貝塞爾曲線演示動畫,t in [0,1]

二次方曲線

為建構二次貝塞爾曲線,可以中介點Q0Q1作為由0至1的t

  • P0P1的連續點Q0,描述一條線性貝塞爾曲線。

  • P1P2的連續點Q1,描述一條線性貝塞爾曲線。

  • Q0Q1的連續點Bt),描述一條二次貝塞爾曲線。


二次貝塞爾曲線的結構
二次貝塞爾曲線演示動畫,t in [0,1]

三次方曲線

為建構高階曲線,便需要相應更多的中介點。對於三次曲線,可由線性貝塞爾曲線描述的中介點Q0Q1Q2,和由二次曲線描述的點R0R1所建構:


三次貝塞爾曲線的結構
三次貝塞爾曲線演示動畫,t in [0,1]

連續性

兩段曲線是否相連接,代表這兩段曲線是否連續的。曲線的連續性分為4種,無連續,點連續,正切連續,曲率連續。下圖分別表示了這幾種情況:

其中曲率連續的曲線過渡的更平滑。我們可以通過參數來設置曲線的連續性。

求值器

OpenGL提供了一些函數來繪制貝塞爾曲線和曲面。我們只需要提供控制點和u,v作為參數,然後調用求值函數來繪制曲線。

2D曲線的例子:

//控制點 GLint numOfPoints = 4; static GLfloat controlPoints[4][3] = {{-4.0f, 0.0f, 0.0f},
{-6.0f, 4.0f, 0.0f},
{6.0f, -4.0f, 0.0f},
{4.0f, 0.0f, 0.0f}}; void SetupRC()
{
  glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
  glColor3f(1.0f, 0.0f, 1.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(0, 0, w, h);
   //使用正交投影
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();

  gluOrtho2D(-10.0f, 10.0f, -10.0f, 10.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();

}

 

在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(100, 0.0f, 100.0f);

  glEvalMesh1(GL_LINE, 0, 100);

 

可以替換下面的代碼

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.0f, 0.0f, 4.0f},    
{ -2.0f, 4.0f, 4.0f},    
{  4.0f, 0.0f, 4.0f }},

{{  -4.0f, 0.0f, 0.0f},    
{ -2.0f, 4.0f, 0.0f},    
{  4.0f, 0.0f, 0.0f }},

{{  -4.0f, 0.0f, -4.0f},    
{ -2.0f, 4.0f, -4.0f},    
{  4.0f, 0.0f, -4.0f }}}; //畫控制點  void DrawPoints(void)
{ int i,j;    

  glColor3f(1.0f, 0.0f, 0.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.0f, 0.0f, 1.0f, 0.0f);
  glRotatef(60.0f, 1.0f, 0.0f, 0.0f);


  glColor3f(0.0f, 0.0f, 1.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);

光照和法線

求值器還可以幫我們生成表面的法線,只需簡單的修改一些代碼:

把glEvalMesh2(GL_LINE, 0, 10, 0, 10);替換為glEvalMesh2(GL_FILL, 0, 10, 0, 10);然後在初始化時 SetupRC中調用glEnable(GL_AUTO_NORMAL);就可以得到一個收到光照的曲面了。

OpenGL超級寶典 第4版 中文版PDF+英文版+源代碼 見  http://www.linuxidc.com/Linux/2013-10/91413.htm

OpenGL編程指南(原書第7版)中文掃描版PDF 下載 http://www.linuxidc.com/Linux/2012-08/67925.htm

OpenGL 渲染篇 http://www.linuxidc.com/Linux/2011-10/45756.htm

Ubuntu 13.04 安裝 OpenGL http://www.linuxidc.com/Linux/2013-05/84815.htm

OpenGL三維球體數據生成與繪制【附源碼】 http://www.linuxidc.com/Linux/2013-04/83235.htm

Ubuntu下OpenGL編程基礎解析 http://www.linuxidc.com/Linux/2013-03/81675.htm

如何在Ubuntu使用eclipse for c++配置OpenGL http://www.linuxidc.com/Linux/2012-11/74191.htm 

更多《OpenGL超級寶典學習筆記》相關知識 見 http://www.linuxidc.com/search.aspx?where=nkey&keyword=34581

Copyright © Linux教程網 All Rights Reserved