国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 學(xué)院 > 開發(fā)設(shè)計(jì) > 正文

基于VC++的OpenGL編程講座之曲線和曲面

2019-11-17 05:16:25
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

  計(jì)算機(jī)圖形學(xué)中,所有的光滑曲線、曲面都采用線段或三角形逼近來(lái)模擬,但為了精確地表現(xiàn)曲線,通常需要成千上萬(wàn)個(gè)線段或三角形來(lái)逼近,這種方法對(duì)于計(jì)算機(jī)的硬件資源有相當(dāng)高的要求。然而,許多有用的曲線、曲面在數(shù)學(xué)上只需要用少數(shù)幾個(gè)參數(shù)(如控制點(diǎn)等)來(lái)描述。這種方法所需要的存儲(chǔ)空間比線段、三角形逼近的方法來(lái)所需要的空間要小得多,并且控制點(diǎn)方法描述的曲線、曲面比線段、三角形逼近的曲線、曲面更精確。
為了說明如何在OpenGL中繪制復(fù)雜曲線和曲面,我們對(duì)上述兩類比方法都進(jìn)行了介紹。下面我們先來(lái)介紹有關(guān)基礎(chǔ)知識(shí),然后再看是如何實(shí)現(xiàn)的吧。

   一、曲線的繪制

  OpenGL通過一種求值器的機(jī)制來(lái)產(chǎn)生曲線和曲面,該機(jī)制非常靈活,可以生成任意角度的多項(xiàng)式曲線,并可以將其他類型的多邊形曲線和曲面轉(zhuǎn)換成貝塞爾曲線和曲面。這些求值器能在任何度的曲線及曲面上計(jì)算指定數(shù)目的點(diǎn)。隨后,OpenGL利用曲線和曲面上的點(diǎn)生成標(biāo)準(zhǔn)OpenGL圖元,例如與曲線或曲面近似的線段和多邊形。由于可讓OpenGL計(jì)算在曲線上所需的任意數(shù)量的點(diǎn),因此可以達(dá)到應(yīng)用所需的精度。
對(duì)于曲線,OpenGL中使用glMap1*()函數(shù)來(lái)創(chuàng)建一維求值器,該函數(shù)原型為:

void glMap1{fd}(GLenum target,TYPE u1,TYPE u2,
GLint stride, GLint order,const TYPE *points);

  函數(shù)的第一個(gè)參數(shù)target指出控制頂點(diǎn)的意義以及在參數(shù)points中需要提供多少值,具體值見表一所示。參數(shù)points指針可以指向控制點(diǎn)集、RGBA顏色值或紋理坐標(biāo)串等。例如若target是GL_MAP1_COLOR_4,則就能在RGBA四維空間中生成一條帶有顏色信息的曲線,這在數(shù)據(jù)場(chǎng)可視化中應(yīng)用極廣。參數(shù)u1和u2,指明變量U的范圍,U一般從0變化到1。參數(shù)stride是跨度,表示在每塊存儲(chǔ)區(qū)內(nèi)浮點(diǎn)數(shù)或雙精度數(shù)的個(gè)數(shù),即兩個(gè)控制點(diǎn)間的偏移量,比如上例中的控制點(diǎn)集ctrpoint[4][3]的跨度就為3,即單個(gè)控制點(diǎn)的坐標(biāo)元素個(gè)數(shù)。函數(shù)參數(shù)order是次數(shù)加 1,叫階數(shù),與控制點(diǎn)數(shù)一致。

參數(shù) 意義 GL_MAP1_VERTEX_3 x,y,z頂點(diǎn)坐標(biāo) GL_MAP1_VERTEX_4 x,y,z,w 頂點(diǎn)坐標(biāo) GL_MAP1_INDEX 顏色表 GL_MAP1_COLOR_4 R,G,B,A GL_MAP1_NORMAL 法向量 GL_MAP1_TEXTURE_COORD_1 s 紋理坐標(biāo) GL_MAP1_TEXTURE_COORD_2 s,t 紋理坐標(biāo) GL_MAP1_TEXTURE_COORD_3 s,t,r 紋理坐標(biāo) GL_MAP1_TEXTURE_COORD_4 s,t,r,q 紋理坐標(biāo)               表一、參數(shù)target的取值表

  使用求值器定義曲線后,必須要啟動(dòng)求值器,才能進(jìn)行下一步的繪制工作。啟動(dòng)函數(shù)仍是glEnable(),其中參數(shù)與glMap1*()的第一個(gè)參數(shù)一致。同樣,關(guān)閉函數(shù)為glDisable(),參數(shù)也一樣。

  一旦啟動(dòng)一個(gè)或多個(gè)求值器,我們就可以構(gòu)造近似曲線了。最簡(jiǎn)單的方法是通過調(diào)用計(jì)算坐標(biāo)函數(shù)glEvalcoord1*()替換所有對(duì)函數(shù)glVertex*()的調(diào)用。與glVertex*()使用二維、三維和四維坐標(biāo)不同,glEvalcoord1*()將u值傳給所有已啟動(dòng)的求值器,然后由這些已啟動(dòng)的求值器生成坐標(biāo)、法向量、顏色或紋理坐標(biāo)。OpenGL曲線坐標(biāo)計(jì)算的函數(shù)形式如下:

void glEvalCoord1{fd}[v](TYPE u);
  該函數(shù)產(chǎn)生曲線坐標(biāo)值并繪制。參數(shù)u是定義域內(nèi)的值,這個(gè)函數(shù)調(diào)用一次只產(chǎn)生一個(gè)坐標(biāo)。在使用glEvalCoord1*()計(jì)算坐標(biāo),因?yàn)閡可取定義域內(nèi)的任意值,所以由此計(jì)算出的坐標(biāo)值也是任意的。

  使用glEvalCoord1*()函數(shù)的優(yōu)點(diǎn)是,可以對(duì)U使用任意值,然而,假如想對(duì)u使用N個(gè)不同的值,就必須對(duì)glEvalCoord1*()函數(shù)執(zhí)行N次調(diào)用,為此,OpenGL提供了等間隔值取值法,即先調(diào)用glMapGrid1*()定義一個(gè)間隔相等的一維網(wǎng)格,然后再用glEvalMesh1()通過一次函數(shù)執(zhí)行,將求值器應(yīng)用在網(wǎng)格上,計(jì)算相應(yīng)的坐標(biāo)值。下面具體解釋這兩個(gè)函數(shù):

  1、void glMapGrid1{fd}(GLint n,TYPE u1,TYPE u2);

  定義一個(gè)網(wǎng)格,從u1到u2分為n步,它們是等間隔的。實(shí)際上,這個(gè)函數(shù)定義的是參數(shù)空間網(wǎng)格。

  2、void glEvalMesh1(GLenum mode,GLint p1,GLint p2);

  計(jì)算并繪制坐標(biāo)點(diǎn)。參數(shù)mode可以是GL_POINT或GL_LINE,即沿曲線繪制點(diǎn)或沿曲線繪制相連的線段。這個(gè)函數(shù)的調(diào)用效果同在p1和p2之間的每一步給出一個(gè)glEvalCoord1()的效果一樣。從編程角度來(lái)說,除了當(dāng)i=0或i=n,它準(zhǔn)確以u(píng)1或u2作為參數(shù)調(diào)用glEvalCoord1()之外,它等價(jià)于一下代碼:

glBegin(GL_POINT); /* glBegin(GL_LINE_STRip); */
   for(i=p1;i <=p2;i++)
     glEvalCoord1(u1+i*(u2-u1)/n);
   glEnd();

  為了進(jìn)一步說明OpenGL中曲線的繪制方法。下面我們來(lái)看一個(gè)簡(jiǎn)單的例子,這是用四個(gè)控制頂點(diǎn)來(lái)畫一條三次Bezier曲線。程序如下(注:這是本講座中提供的第一個(gè)完整的OpenGL實(shí)例代碼,假如讀者朋友對(duì)整個(gè)程序結(jié)構(gòu)有些迷惑的話,也不要緊,慢慢地往下看,先有一個(gè)感官上的印象,主要是把握如何實(shí)現(xiàn)曲線繪制這一部分。關(guān)于OpenGL的程序整體結(jié)構(gòu)實(shí)現(xiàn),筆者將在第五講中專門闡述):

#include "glos.h"
#include
#include
#include
void myinit(void);
void CALLBACK myReshape(GLsizei w, GLsizei h);
void CALLBACK display(void);
GLfloat ctrlpoints[4][3] = {
   { -4.0, -4.0, 0.0 }, { -2.0, 4.0, 0.0 },
   { 2.0, -4.0, 0.0 }, { 4.0, 4.0, 0.0 }
};
void myinit(void)
{
   glClearColor(0.0, 0.0, 0.0, 1.0);
glMap1f(GL_MAP1_VERTEX_3, 0.0, 1.0, 3, 4,
&ctrlpoints[0][0]);
   glEnable(GL_MAP1_VERTEX_3);
   glShadeModel(GL_FLAT);
}
void CALLBACK display(void)
{
   int i;
   glClear(GL_COLOR_BUFFER_BIT);
   glColor3f(1.0, 1.0, 1.0);
   glBegin(GL_LINE_STRIP);
   for (i = 0; i <= 30; i++)
      glEvalCoord1f((GLfloat) i/30.0);
   glEnd();
   /* 顯示控制點(diǎn) */
   glPointSize(5.0);
   glColor3f(1.0, 1.0, 0.0);
   glBegin(GL_POINTS);
   for (i = 0; i < 4; i++)
     glVertex3fv(&ctrlpoints[i][0]);
   glEnd();
  glFlush();
}
void CALLBACK myReshape(GLsizei w, GLsizei h)
{
  glViewport(0, 0, w, h);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  if (w <= h)
glOrtho(-5.0, 5.0, -5.0*(GLfloat)h/(GLfloat)w,
5.0*(GLfloat)h/(GLfloat)w, -5.0, 5.0);
  else
glOrtho(-5.0*(GLfloat)w/(GLfloat)h, 5.0*(GLfloat)w/(GLfloat)h, -5.0, 5.0, -5.0, 5.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
   }

二、曲面構(gòu)造

  曲面的繪制方法基本上與曲線的繪制方法是相同的,所不同的是曲面使用二維求值器,并且控制點(diǎn)連接起來(lái)形成一個(gè)網(wǎng)格。

  對(duì)于曲面,求值器除了使用二個(gè)參數(shù)U、V之外,其余與一維求值器基本相同。頂點(diǎn)坐標(biāo) 、顏色、法線矢量和紋理坐標(biāo)都對(duì)應(yīng)于曲面而不是曲線。在OpenGL中定義二維求值器的函數(shù)是:

void glMap2{fd}(GLenum target,TYPE u1,TYPE u2,GLint ustride,GLint uorder,TYPE v1,TYPE v2,
GLint vstride,GLint vorder,TYPE points);

  參數(shù)target可以是表一中任意值,不過需將MAP1改為MAP2。同樣,啟動(dòng)曲面的函數(shù)仍是glEnable(),關(guān)閉是glDisable()。u1、u2為u的最大值和最小值;v1、v2為v的最大值和最小值。參數(shù)ustride和vstride指出在控制點(diǎn)數(shù)組中u和v向相鄰點(diǎn)的跨度,即可從一個(gè)非常大的數(shù)組中選擇一塊控制點(diǎn)長(zhǎng)方形。例如,若數(shù)據(jù)定義成如下形式:

GLfloat ctlpoints[100][100][3];
  并且,要用從ctlpoints[20][30]開始的4x4子集,選擇ustride為100*3,vstride為3,初始點(diǎn)設(shè)置為ctlpoints[20][30][0]。最后的參數(shù)都是階數(shù),uorder和vorder,二者可以不同。

  曲面坐標(biāo)計(jì)算函數(shù)為:

void glEvalCoord2{fd}[v](TYPE u,TYPE v);
  該函數(shù)產(chǎn)生曲面坐標(biāo)并繪制。參數(shù)u和v是定義域內(nèi)的值。下面看一個(gè)繪制Bezier曲面的例子:

/* 控制點(diǎn)的坐標(biāo) */
   GLfloat ctrlpoints[4][4][3] = {
{{-1.5, -1.5, 2.0}, {-0.5, -1.5, 2.0},
  {0.5, -1.5, -1.0}, {1.5, -1.5, 2.0}},
  {{-1.5, -0.5, 1.0}, {-0.5, 1.5, 2.0},
{0.5, 0.5, 1.0}, {1.5, -0.5, -1.0}},
{{-1.5, 0.5, 2.0}, {-0.5, 0.5, 1.0},
{0.5, 0.5, 3.0}, {1.5, -1.5, 1.5}},
{{-1.5, 1.5, -2.0}, {-0.5, 1.5, -2.0},
{0.5, 0.5, 1.0}, {1.5, 1.5, -1.0}}
};
void myinit(void)
{
     glClearColor (0.0, 0.0, 0.0, 1.0);
     glMap2f(GL_MAP2_VERTEX_3, 0, 1, 3, 4, 0, 1, 12, 4,
&ctrlpoints[0][0][0]);
glEnable(GL_MAP2_VERTEX_3);
glMapGrid2f(20, 0.0, 1.0, 20, 0.0, 1.0);
glEnable(GL_DEPTH_TEST);
   }
   void CALLBACK display(void)
   {
     int i, j;
glClear(GL_COLOR_BUFFER_BIT GL_DEPTH_BUFFER_BIT);
     glColor3f(0.3, 0.6, 0.9);
     glPushMatrix ();
     glRotatef(35.0, 1.0, 1.0, 1.0);
     for (j = 0; j <= 8; j++)
     {
       glBegin(GL_LINE_STRIP);
       for (i = 0; i <= 30; i++)
         glEvalCoord2f((GLfloat)i/30.0, (GLfloat)j/8.0);
       glEnd();
       glBegin(GL_LINE_STRIP);
         for (i = 0; i <= 30; i++)
           glEvalCoord2f((GLfloat)j/8.0,
(GLfloat)i/30.0);
       glEnd();
     }
     glPopMatrix ();
     glFlush();
   }

  OpenGL中定義均勻間隔的曲面坐標(biāo)值的函數(shù)與曲線的類似,其函數(shù)形式為:

void glMapGrid2{fd}(GLenum nu,TYPE u1,TYPE u2,GLenum nv,TYPE v1,TYPE v2);
void glEvalMesh2(GLenum mode,GLint p1,GLint p2,GLint q1,GLint q2);

  第一個(gè)函數(shù)定義參數(shù)空間的均勻網(wǎng)格,從u1到u2分為等間隔的nu步,從v1到v2分為等間隔的nv步,然后glEvalMesh2()把這個(gè)網(wǎng)格應(yīng)用到已經(jīng)啟動(dòng)的曲面計(jì)算上。第二個(gè)函數(shù)參數(shù)mode除了可以是GL_POINT和GL_LINE外,還可以是GL_FILL,即生成填充空間曲面。

  下面舉出一個(gè)用網(wǎng)格繪制一個(gè)經(jīng)過光照和明暗處理的Bezier曲面的例程:


#include "glos.h"
#include
#include
#include
void myinit(void);
void initlights(void);
void CALLBACK myReshape(GLsizei w, GLsizei h);
void CALLBACK display(void);
/* 控制點(diǎn)坐標(biāo) */
GLfloat ctrlpoints[4][4][3] = {
   {{-1.5, -1.5, 2.0}, {-0.5, -1.5, 2.0},
  {0.5, -1.5, -1.0}, {1.5, -1.5, 2.0}},
  {{-1.5, -0.5, 1.0}, {-0.5, 1.5, 2.0},
{0.5, 0.5, 1.0}, {1.5, -0.5, -1.0}},
{{-1.5, 0.5, 2.0}, {-0.5, 0.5, 1.0},
{0.5, 0.5, 3.0}, {1.5, -1.5, 1.5}},
{{-1.5, 1.5, -2.0}, {-0.5, 1.5, -2.0},
{0.5, 0.5, 1.0}, {1.5, 1.5, -1.0}}
};
void initlights(void)
{
   GLfloat ambient[] = { 0.4, 0.6, 0.2, 1.0 };
   GLfloat position[] = { 0.0, 1.0, 3.0, 1.0 };
   GLfloat mat_diffuse[] = { 0.2, 0.4, 0.8, 1.0 };
   GLfloat mat_specular[] = { 1.0, 1.0, 1.0, 1.0 };
   GLfloat mat_shininess[] = { 80.0 };
   glEnable(GL_LIGHTING);
   glEnable(GL_LIGHT0);
   glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
   glLightfv(GL_LIGHT0, GL_POSITION, position);
glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
  glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
}
void CALLBACK display(void)
{
  glClear(GL_COLOR_BUFFER_BIT GL_DEPTH_BUFFER_BIT);
  glPushMatrix();
  glRotatef(35.0, 1.0, 1.0, 1.0);
  glEvalMesh2(GL_FILL, 0, 20, 0, 20);
  glPopMatrix();
  glFlush();
}
void myinit(void)
{
     glClearColor (0.0, 0.0, 0.0, 1.0);
   glEnable (GL_DEPTH_TEST);
  glMap2f(GL_MAP2_VERTEX_3, 0, 1, 3, 4, 0, 1, 12,
4, &ctrlpoints[0][0][0]);
  glEnable(GL_MAP2_VERTEX_3);
  glEnable(GL_AUTO_NORMAL);
  glEnable(GL_NORMALIZE);
  glMapGrid2f(20, 0.0, 1.0, 20, 0.0, 1.0);
initlights();
}
void CALLBACK myReshape(GLsizei w, GLsizei h)
{
   glViewport(0, 0, w, h);
   glMatrixMode(GL_PROJECTION);
   glLoadIdentity();
   if (w <= h)
glOrtho(-4.0, 4.0, -4.0*(GLfloat)h/(GLfloat)w,
4.0*(GLfloat)h/(GLfloat)w, -4.0, 4.0);
else
  glOrtho(-4.0*(GLfloat)w/(GLfloat)h,
4.0*(GLfloat)w/(GLfloat)h, -4.0, 4.0, -4.0, 4.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void main(void)
{
   auxInitDisplayMode (AUX_SINGLE AUX_RGBA);
  auxInitPosition (0, 0, 500, 500);
  auxInitWindow ("Lighted and Filled Bezier Surface");
  myinit();
  auxReshapeFunc (myReshape);
  auxMainLoop(display);
}

三、圖元逼近法繪制三維物體
  在OpenGL的輔助庫(kù)中,提供了繪制11種基本幾何圖形的函數(shù),具體參考第一講的有關(guān)內(nèi)容,在此不再贅述。這里我們討論用另外一種方法來(lái)繪制三維物體 。
  需要注重的是,這里我們用來(lái)近似曲面的多邊形最好選擇三角形,而不是四邊形或其他外形的多邊形,這是因?yàn)槿切蔚娜齻€(gè)頂點(diǎn)在任何時(shí)候都位于同一平面內(nèi),它一定是非常簡(jiǎn)單的非凹多邊形,而四邊形或其他多邊形的頂點(diǎn)可能不在同一平面內(nèi),也就有可能不是簡(jiǎn)單多邊形,對(duì)于這樣的多邊形,OpenGL是不能正常處理的。假設(shè)我們繪制一個(gè)球體,球體表面用很多個(gè)小三角形拼接而成,顯然,用來(lái)近似球面的三角形越小、三角形越多,那么球面就越光滑。為了簡(jiǎn)要地說明如何用三角形逼近球體,這里我們使用三角形來(lái)構(gòu)造一個(gè)20面體,二十面體的頂點(diǎn)坐標(biāo)定義在vdata[][]數(shù)組中,tindinces[][]數(shù)組定義了構(gòu)成二十面體的二十個(gè)三角形頂點(diǎn)的繪制順序。下面是主要實(shí)現(xiàn)代碼:
#define x 5.25731
#define z 8.50651
static GLfloat vdata[12][3]={
{x,0.0,z},{x,0.0,z},{-x,0.0,-z},{x,0.0,-z},
{0.0,z,x},{0.0,z,-x},{0.0,-z-x},{0.0,-z,-x},
{z,x,0.0},{-z,x,0.0},{z,-x,0.0},{-z,-x,0.0}
};
static GLint tindices[20][3]={
{0,4,1},{0,9,4},{9,5,4},{4,5,8},{4,8,1},
{8,10,1},{8,3,10},{5,3,8},{5,2,3},{2,7,3},
{7,10,3},{7,6,10},{7,11,6},{11,0,6},
{6,1,10},{9,0,11},{9,11,2},{9,2,5},{7,2,11}
};
glColor3f(1.0, 0.0, 0.0);
for(int i=0;i <20;i++){
glBegin(GL_TRIANGLES);
glVertex3fv(&vdata[tindices[i][0]][0]);
glVertex3fv(&vdata[tindices[i][1]][0]);
glVertex3fv(&vdata[tindices[i][2]][0]);
glEnd();
}

  顯然,用正二十面體來(lái)表示一個(gè)球體顯得過于粗糟,可以通過增加面數(shù)的方法使正多面體和求更為接近,一種簡(jiǎn)單的方法是剖分法,即將前面定義的三角形面分成幾個(gè)面,例如,一分為四,形成4個(gè)多邊形等,具體實(shí)現(xiàn)方法這里就不再贅述了。

上一篇:實(shí)例講解

下一篇:C++ 友元(friend)

發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 泊头市| 顺昌县| 冕宁县| 临颍县| 蓝山县| 富顺县| 五常市| 凉山| 汶川县| 六盘水市| 光山县| 布拖县| 武城县| 德惠市| 河北省| 布拖县| 偃师市| 武清区| 灌南县| 台北市| 竹溪县| 华阴市| 南丰县| 高州市| 临海市| 凌源市| 高雄市| 南川市| 灵丘县| 平顺县| 晋宁县| 西华县| 栾城县| 湖南省| 海伦市| 西和县| 延吉市| 德化县| 三门峡市| 阳信县| 厦门市|