OpenGL之曲线和曲面.docx

上传人:b****1 文档编号:23259219 上传时间:2023-05-15 格式:DOCX 页数:10 大小:19.77KB
下载 相关 举报
OpenGL之曲线和曲面.docx_第1页
第1页 / 共10页
OpenGL之曲线和曲面.docx_第2页
第2页 / 共10页
OpenGL之曲线和曲面.docx_第3页
第3页 / 共10页
OpenGL之曲线和曲面.docx_第4页
第4页 / 共10页
OpenGL之曲线和曲面.docx_第5页
第5页 / 共10页
点击查看更多>>
下载资源
资源描述

OpenGL之曲线和曲面.docx

《OpenGL之曲线和曲面.docx》由会员分享,可在线阅读,更多相关《OpenGL之曲线和曲面.docx(10页珍藏版)》请在冰豆网上搜索。

OpenGL之曲线和曲面.docx

OpenGL之曲线和曲面

计算机图形学中,所有的光滑曲线、曲面都采用线段或三角形逼近来模拟,但为了精确地表现曲线,通常需要成千上万个线段或三角形来逼近,这种方法对于计算机的硬件资源有相当高的要求。

然而,许多有用的曲线、曲面在数学上只需要用少数几个参数(如控制点等)来描述。

这种方法所需要的存储空间比线段、三角形逼近的方法来所需要的空间要小得多,并且控制点方法描述的曲线、曲面比线段、三角形逼近的曲线、曲面更精确。

  为了说明如何在OpenGL中绘制复杂曲线和曲面,我们对上述两类比方法都进行了介绍。

下面我们先来介绍有关基础知识,然后再看是如何实现的吧。

  一、曲线的绘制

  OpenGL通过一种求值器的机制来产生曲线和曲面,该机制非常灵活,可以生成任意角度的多项式曲线,并可以将其他类型的多边形曲线和曲面转换成贝塞尔曲线和曲面。

这些求值器能在任何度的曲线及曲面上计算指定数目的点。

随后,OpenGL利用曲线和曲面上的点生成标准OpenGL图元,例如与曲线或曲面近似的线段和多边形。

由于可让OpenGL计算在曲线上所需的任意数量的点,因此可以达到应用所需的精度。

对于曲线,OpenGL中使用glMap1*()函数来创建一维求值器,该函数原型为:

voidglMap1{fd}(GLenumtarget,TYPEu1,TYPEu2,GLintstride,GLintorder,constTYPE*points);

  函数的第一个参数target指出控制顶点的意义以及在参数points中需要提供多少值,具体值见表一所示。

参数points指针可以指向控制点集、RGBA颜色值或纹理坐标串等。

例如若target是GL_MAP1_COLOR_4,则就能在RGBA四维空间中生成一条带有颜色信息的曲线,这在数据场可视化中应用极广。

参数u1和u2,指明变量U的范围,U一般从0变化到1。

参数stride是跨度,表示在每块存储区内浮点数或双精度数的个数,即两个控制点间的偏移量,比如上例中的控制点集ctrpoint[4][3]的跨度就为3,即单个控制点的坐标元素个数。

函数参数order是次数加1,叫阶数,与控制点数一致。

参数

意义

GL_MAP1_VERTEX_3

x,y,z顶点坐标

GL_MAP1_VERTEX_4

x,y,z,w顶点坐标

GL_MAP1_INDEX

颜色表

GL_MAP1_COLOR_4

R,G,B,A

GL_MAP1_NORMAL

法向量

GL_MAP1_TEXTURE_COORD_1

s 纹理坐标

GL_MAP1_TEXTURE_COORD_2

s,t 纹理坐标

GL_MAP1_TEXTURE_COORD_3

s,t,r 纹理坐标

GL_MAP1_TEXTURE_COORD_4

s,t,r,q 纹理坐标

              表一、参数target的取值表

  使用求值器定义曲线后,必须要启动求值器,才能进行下一步的绘制工作。

启动函数仍是glEnable(),其中参数与glMap1*()的第一个参数一致。

同样,关闭函数为glDisable(),参数也一样。

  一旦启动一个或多个求值器,我们就可以构造近似曲线了。

最简单的方法是通过调用计算坐标函数glEvalcoord1*()替换所有对函数glVertex*()的调用。

与glVertex*()使用二维、三维和四维坐标不同,glEvalcoord1*()将u值传给所有已启动的求值器,然后由这些已启动的求值器生成坐标、法向量、颜色或纹理坐标。

OpenGL曲线坐标计算的函数形式如下:

voidglEvalCoord1{fd}[v](TYPEu);

  该函数产生曲线坐标值并绘制。

参数u是定义域内的值,这个函数调用一次只产生一个坐标。

在使用glEvalCoord1*()计算坐标,因为u可取定义域内的任意值,所以由此计算出的坐标值也是任意的。

  使用glEvalCoord1*()函数的优点是,可以对U使用任意值,然而,如果想对u使用N个不同的值,就必须对glEvalCoord1*()函数执行N次调用,为此,OpenGL提供了等间隔值取值法,即先调用glMapGrid1*()定义一个间隔相等的一维网格,然后再用glEvalMesh1()通过一次函数执行,将求值器应用在网格上,计算相应的坐标值。

下面详细解释这两个函数:

  1、voidglMapGrid1{fd}(GLintn,TYPEu1,TYPEu2);

  定义一个网格,从u1到u2分为n步,它们是等间隔的。

实际上,这个函数定义的是参数空间网格。

  2、voidglEvalMesh1(GLenummode,GLintp1,GLintp2);

  计算并绘制坐标点。

参数mode可以是GL_POINT或GL_LINE,即沿曲线绘制点或沿曲线绘制相连的线段。

这个函数的调用效果同在p1和p2之间的每一步给出一个glEvalCoord1()的效果一样。

从编程角度来说,除了当i=0或i=n,它准确以u1或u2作为参数调用glEvalCoord1()之外,它等价于一下代码:

glBegin(GL_POINT); /*glBegin(GL_LINE_STRIP);*/

  for(i=p1;i<=p2;i++)

    glEvalCoord1(u1+i*(u2-u1)/n);

  glEnd();

  为了进一步说明OpenGL中曲线的绘制方法。

下面我们来看一个简单的例子,这是用四个控制顶点来画一条三次Bezier曲线。

程序如下(注:

这是本讲座中提供的第一个完整的OpenGL实例代码,如果读者朋友对整个程序结构有些迷惑的话,也不要紧,慢慢地往下看,先有一个感官上的印象,主要是掌握如何实现曲线绘制这一部分。

关于OpenGL的程序整体结构实现,笔者将在第五讲中专门阐述):

#include"glos.h"

#include

#include

#include

voidmyinit(void);

voidCALLBACKmyReshape(GLsizeiw,GLsizeih);

voidCALLBACKdisplay(void);

GLfloatctrlpoints[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}

};

voidmyinit(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);

}

voidCALLBACKdisplay(void)

{

  inti;

  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();

  /*显示控制点*/

  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();

}

voidCALLBACKmyReshape(GLsizeiw,GLsizeih)

{

 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();

  }

二、曲面构造

  曲面的绘制方法基本上与曲线的绘制方法是相同的,所不同的是曲面使用二维求值器,并且控制点连接起来形成一个网格。

  对于曲面,求值器除了使用二个参数U、V之外,其余与一维求值器基本相同。

顶点坐标、颜色、法线矢量和纹理坐标都对应于曲面而不是曲线。

在OpenGL中定义二维求值器的函数是:

voidglMap2{fd}(GLenumtarget,TYPEu1,TYPEu2,GLintustride,GLintuorder,TYPEv1,TYPEv2,

GLintvstride,GLintvorder,TYPEpoints);

  参数target可以是表一中任意值,不过需将MAP1改为MAP2。

同样,启动曲面的函数仍是glEnable(),关闭是glDisable()。

u1、u2为u的最大值和最小值;v1、v2为v的最大值和最小值。

参数ustride和vstride指出在控制点数组中u和v向相邻点的跨度,即可从一个非常大的数组中选择一块控制点长方形。

例如,若数据定义成如下形式:

GLfloatctlpoints[100][100][3];

  并且,要用从ctlpoints[20][30]开始的4x4子集,选择ustride为100*3,vstride为3,初始点设置为ctlpoints[20][30][0]。

最后的参数都是阶数,uorder和vorder,二者可以不同。

  曲面坐标计算函数为:

voidglEvalCoord2{fd}[v](TYPEu,TYPEv);

  该函数产生曲面坐标并绘制。

参数u和v是定义域内的值。

下面看一个绘制Bezier曲面的例子:

/*控制点的坐标*/

  GLfloatctrlpoints[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}}

};

voidmyinit(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);

  }

  voidCALLBACKdisplay(void)

  {

    inti,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中定义均匀间隔的曲面坐标值的函数与曲线的类似,其函数形式为:

voidglMapGrid2{fd}(GLenumnu,TYPEu1,TYPEu2,GLenumnv,TYPEv1,TYPEv2);

voidglEvalMesh2(GLenummode,GLintp1,GLintp2,GLintq1,GLintq2);

  第一个函数定义参数空间的均匀网格,从u1到u2分为等间隔的nu步,从v1到v2分为等间隔的nv步,然后glEvalMesh2()把这个网格应用到已经启动的曲面计算上。

第二个函数参数mode除了可以是GL_POINT和GL_LINE外,还可以是GL_FILL,即生成填充空间曲面。

  下面举出一个用网格绘制一个经过光照和明暗处理的Bezier曲面的例程:

#include"glos.h"

#include

#include

#include

voidmyinit(void);

voidinitlights(void);

voidCALLBACKmyReshape(GLsizeiw,GLsizeih);

voidCALLBACKdisplay(void);

/*控制点坐标*/

GLfloatctrlpoints[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}}

};

voidinitlights(void)

{

  GLfloatambient[]={0.4,0.6,0.2,1.0};

  GLfloatposition[]={0.0,1.0,3.0,1.0};

  GLfloatmat_diffuse[]={0.2,0.4,0.8,1.0};

  GLfloatmat_specular[]={1.0,1.0,1.0,1.0};

  GLfloatmat_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);

}

voidCALLBACKdisplay(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();

}

voidmyinit(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();

}

voidCALLBACKmyReshape(GLsizeiw,GLsizeih)

{

  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();

}

voidmain(void)

{

  auxInitDisplayMode(AUX_SINGLE|AUX_RGBA);

 auxInitPosition(0,0,500,500);

 auxInitWindow("LightedandFilledBezierSurface");

 myinit();

 auxReshapeFunc(myReshape);

 auxMainLoop(display);

}

三、图元逼近法绘制三维物体

  在OpenGL的辅助库中,提供了绘制11种基本几何图形的函数,具体参考第一讲的有关内容,在此不再赘述。

这里我们讨论用另外一种方法来绘制三维物体。

  需要注意的是,这里我们用来近似曲面的多边形最好选择三角形,而不是四边形或其他形状的多边形,这是因为三角形的三个顶点在任何时候都位于同一平面内,它一定是非常简单的非凹多边形,而四边形或其他多边形的顶点可能不在同一平面内,也就有可能不是简单多边形,对于这样的多边形,OpenGL是不能正常处理的。

假设我们绘制一个球体,球体表面用很多个小三角形拼接而成,显然,用来近似球面的三角形越小、三角形越多,那么球面就越光滑。

为了简要地说明如何用三角形逼近球体,这里我们使用三角形来构造一个20面体,二十面体的顶点坐标定义在vdata[][]数组中,tindinces[][]数组定义了构成二十面体的二十个三角形顶点的绘制顺序。

下面是主要实现代码:

#definex5.25731

#definez8.50651

staticGLfloatvdata[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}

};

staticGLinttindices[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(inti=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();

}

  显然,用正二十面体来表示一个球体显得过于粗糟,可以通过增加面数的方法使正多面体和求更为接近,一种简单的方法是剖分法,即将前面定义的三角形面分成几个面,例如,一分为四,形成4个多边形等,具体实现方法这里就不再赘述了。

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 表格模板 > 表格类模板

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1