OpenGL Bezier曲线及曲面生成

文章出处:http://www.pinxue.net/OpenGL/credbook/evaluator.html

 

 

思路:

1、定义定值器指定:定值器类型,
    每个方向的起止范围、次数(控制点数),
    每个方向步进一个单位对应的浮点值个数
    控制点清单。
2、打开定值器viod glEnable(定值器类型);
3、引用定定值器    glEvalCoord(Ui,Vi);当前生成的点的值域
    注意与1、中取得一致,如果总的值域
为[0,1],则100个点组成的曲线上第10点的
值域为(1-0)*10/100

  贝泽尔曲线是一个单变量矢量函数:C(u) = [X(u) Y(u) Z(u)] 其中u在某个值域中变化(如[0,1]),

  贝泽尔曲面片是双变量矢量函数:S(u,v) = [X(u,v) Y(u,v) Z(u,v)] 其中uv都在某个值域中变化。排列不一定非要象这所展示的那样是三维的。例如,你也许想在一个平面或纹理坐标中输出两维输出值,或许你想在描述RGBA信息时得到四维值。甚至在处理灰度级时一维输出值也是合理的。

 

  对每个u(或uv,在曲面中),C()S()的公式计算得出曲线或曲面上的一个点。要使用一个定值器,首先定义函数C()S(),启用它(Enable),然后用glEvalCoord1()glEvalCoord2()命令取代glVertex()。这样,曲线或曲面的顶点就可以和其它任何顶点一样地使用,如用于形成点或线。此外,别一些命令自动生成一系列的顶点,用心生成一个在u上等分的网格。一维和二维定值器是相似的,但一维的描述要简单些,因此先说它。

 

一维定值器:

  这部分提供了一个使用一维定值器以绘制一个曲线的例子。其中控制曲线的的命令和公式稍后说明。

1-D例子:一个简单的贝泽尔曲线

例子11-1所展示的程序用4个控制点绘制一条立方贝泽尔曲线,如图所示:

 (figure 11-1:一条贝泽尔曲线

 

Example 11-1:用4个控制点绘制一条立方贝泽尔曲线:bezcurve.c

//------------------------------------------------------------------------------------

#include <GL/gl.h>

#include <GL/glu.h>

#include "aux.h"

 

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 display(void)

{

int i;

 

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_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();

/* The following code displays the control points as dots. */

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

}

int main(int argc, char** argv)

{

auxInitDisplayMode (AUX_SINGLE | AUX_RGBA);

auxInitPosition (0, 0, 500, 500);

auxInitWindow (argv[0]);

myinit();

auxReshapeFunc (myReshape);

auxMainLoop(display);

}

//------------------------------------------------------------------------------------

一条立方贝泽尔曲线由四个控制点描述,这些控制点出现在本例的ctrlpoints[ ][ ]数组中。这个数组是传给glMap1f()的参数之一。该命令的所有参数如下:

GL_MAP1_VERTEX_3 :产生三维顶点

0 :参数u的最小值

1 :参数u的最大值

3 :每前进一个控制点应在生成数据中前进的浮点值个数

4 :spline的次数+1;在本例中度数是3(因为是立方曲线)

&ctrlpoints[0][0] :指向第一个控制点数据的指针

 

  注意,第二、三个参数控制了曲线的参数化--即变量u从0变到1时曲线从一端走到另一端。对glEnalbe()的调用为产生二维顶点而打开(enable)一维定值器。

  曲线是在例程display()中的glBegin()glEnd()调用之间绘制的。由于定值器是打开的,命令glEvalCoord1f()就象发出了一条带有坐标参数的glVertex()命令一样,这些坐标就是曲线上对应于输入参数u的顶点的坐标。

  定义并评估一个一维定值器

  n度(或n+1次)伯恩斯坦(Bernstein)多项式由由下式给定:

如果Pi描述一套控制点(1、2、3维甚至4维),那么,公式

u0变到1时就描述了一条贝泽尔曲线。要描述相同的曲线而允许uu1u2间变化,定值:

 

命令glMap1()定义一个使用这些公式的一维定值器。

Void glMap1{fd}(Glenum target, TYPEu1, TYPEu2, Glint stride, Glint order, const TYPE *points);

  定义一个一维定值器。Target参数指明控制点代表什么,清单见Table 11-1,以及在points中需要提供多少个值。这些点可以代表顶点、RGBA颜色数据、法向量或纹理坐标。例如,用GL_MAP1_COLOR_4,定值器生成属于某一条曲线的四维色彩空间中的颜色数据。在引用定值器之前,你也用Table 11-1列出的参数值打开每个定义好的定值器。传递适当的值给glEnable()或glDisable()以打开或禁用定值器。

  glMap1*()的第二个参数,u1和u2,指出了变量u的范围。变量stride是每个存储器块中单精度或双精度值(根据需要)的个数。即,它是一个控制点开始到下一个控制点开始间的偏移值。

  orders是度数加一,它应当与控制点数一致。pionts参数指向第一个控制点的第一个坐标。例如将数据结构传给glMap1*()时,对pionts使用如下的值:
(GLfloat)(&ctlpoints[0].x)

Table 11-1 : Types of Control Points for Use with glMap1*()

ParameteMeaning
GL_MAP1_VERTEX_3x, y, z vertex coordinates
GL_MAP1_VERTEX_4x, y, z, w vertex coordinates
GL_MAP1_INDEXcolor index
GL_MAP1_COLOR_4R, G, B, A
GL_MAP1_NORMALnormal coordinates
GL_MAP1_TEXTURE_COORD_1s texture coordinates
GL_MAP1_TEXTURE_COORD_2s, t texture coordinates
GL_MAP1_TEXTURE_COORD_3s, t, r texture coordinates
GL_MAP1_TEXTURE_COORD_4s, t, r, q texture coordinates

  一次可以有多个定值器被定值。例如,如果你同时定义并打开GL_MAP1_VERTEX_3和GL_MAP1_COLOR_4两个定值器,然后调用glEvalCoord1()同时生成一个位置和一个颜色。同一时间,只可有一个顶点定值器打开,尽管你或许已经定义两个。类似的,只能有一个纹理定值器可能是活动的。然而不同的是,定值器可以用于生成(顶点、法向、颜色和纹理坐标)数据的任意组合。如果同一类型的多个定值器被定义并打开,只有维数最高的那个被使用。

使用glEvalCoord1*()以定值一个已经定义并打开的一维图(map)。
void glEvalCoord1{fd}{v}(TYPE u);
引起对打开的一维图进行定值。参数u是坐标值域的值(或指针,用于本命令的向量版)。

定义一维坐标的等分空间值

  你可以用任意的u值调用glEvalCoord1(),但通常使用等分空间值(evenly spaced value),正如前边例子Example 11-1所示。为获取等分空间值,用glMapGrid1*()定义一个一维格子,用glEvalMesh1()应用它。   
void glMapGrid1{fd}(GLint n, TYPE u1, TYPE u2);定义一个分n步从u1进到u2的格子,这个格子是等分空间的。

void glEvalMesh1(GLenum mode, GLint p1, GLint p2);
将当前已经定义的图的格子应用于所有打开的定值器。mode可以是GL_POINT或GL_LINE,看你是想画曲线上的点还是相连的线了。这个调用与在[p1p2]之间每一步发一条glEvalCoord1()作用完全相同,其中0<=p1,p2<=n。从程序的角度年,它等价于:

glBegin(GL_POINTS); /* OR glBegin(GL_LINE_STRIP); */
for (i = p1; i <= p2; i++) 
glEvalCoord1(u1 + i*(u2-u1)/n);
glEnd();

例外:如果i=0或i=n,那么glEvalCoord()将以u1或u2作为参数被调用。


两维定值器

二维的每件事与一维时的情况相似,除了所有的命令都必须用两个参数,u和v。点.颜色.法向量或纹理坐标都必须作用于一个曲面而非曲线。数学点,贝泽尔曲面下这个式子指定:Image23.gif (1241 bytes)
其中,Pij是一套m*n的控制点,Bi是与一维时相同的伯恩斯坦多项式。正如前面所述,Pij可惟代表顶点、法向量、颜色或纹理坐标。

使用二维定值器的过程与一维相似:
1.定义定值器,用glMap2*()
2.打开定值器,传适当的值给glEnalbe()
3.在glBegin/glEnd()对之间调用glEvalCoord2()来引用这些定值器。

定义并定值一个二维定值器

用glMap2*()和glEvalCoord2*()以定义然后引用一个两维定值器。
void glMap2{fd}(GLenum target, TYPE u1, TYPE u2, GLint ustride, GLint uorder, 
             TYPE v1, TYPE v2, GLint vstride, GLint vorder, TYPE pionts);
target参数可以取Table 11-1中的任何一个值,注意用MAP2代替MAP1。如前所述,这些值也与glEnabled()一起用以打开相应的定值器。u和v的最小和最多值由u1,u2,v1,v2提供。参数ustride和vstride说明了一套单(双)精度值(看情况)的个数,这套值允许用户从较大的控制点数组中选择一个子块。例如,如果在表单(form)中出现了如下数据:
GLfloat ctlpoints[100][100][3];
并且你想用人tlpoints[20[30]开始的一个4X4子集,选择ustride为100*3,且vstride为3。启点,points,应该设为&ctlpoints[20[30][0],最后,次元参数uorder和vorder可以不同,允许在一个方向上为三次而另一个方向上为二次的曲面片,例如:
void glEvalCoord2{fd}{v}(TYPE u, TYPE v);
引起对打开的二维图的定值。参数u和v是坐标值域的值(或指向值的指针,用于命令的矢量版)。如果顶点定值器中的一个被打开了(GL_MAP2_VERTEX_3或GL_MAP2_VERTEX_4),那么曲面的法向将被分析计算。这个法向量是与生成的顶点相关联的,如果自动法向量生成已经打开的话(通过以GL_AUTO_NORMAL调用glEnalbe())。如果它被关闭了,相应的打开的法向量图将被用于产生一个法向量。如果没有这样的图存在,那么使用当前法向量。

二维例子:一个贝泽尔曲面

Example 11-2用定值器绘制一个线框的贝泽尔曲面,如图Figure 11-2所示。在样例中,曲面是在每个方向上用9条曲线绘成的。每条曲线画成30段。要得到完整的程序请将例子Example 11-1中的myReshape()和main()例子加进来。
bezsurf.gif (1997 bytes)

Figure 11-2:一个贝泽尔曲面

Example 11-2:画一个贝泽尔曲面

#include <GL/gl.h>
#include <GL/glu.h>
#include "aux.h"

GLfloat ctrlpoints[4][4][3] = {
{{-1.5, -1.5, 4.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, -0.5, 3.0}, 
{0.5, -0.5, 0.0}, {1.5, -0.5, -1.0}}, 
{{-1.5, 0.5, 4.0}, {-0.5, 0.5, 0.0}, 
{0.5, 0.5, 3.0}, {1.5, 0.5, 4.0}}, 
{{-1.5, 1.5, -2.0}, {-0.5, 1.5, -2.0}, 
{0.5, 1.5, 0.0}, {1.5, 1.5, -1.0}}
};

void display(void)
{
int i, j;

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glColor3f(1.0, 1.0, 1.0);
glPushMatrix ();
glRotatef(85.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();
}

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);
glEnable(GL_DEPTH_TEST);
glShadeModel(GL_FLAT);
}

定义二维空间等分坐标

在二维中,glMapGrid2*()和glEvalMesh2()命令与一维版是相似的,除了必须同时包含u和v的信息。
void glMapGrid2{fd}(GLint nu, TYPE u1, TYPE u2, GLint nv, TYPE v1, TYPE v2);
void glEvalMesh2(GLenum mode, GLint p1, GLint p2, GLint q1, GLint q2);

定义一个二维图网格,按nu个空间等分步从u1进到u2、分nv步从v1v2(glMapGrid2*()),然后将这个网格作用于所有打开的定值器(glEvalMesh2())。这两个命令与一维时唯一明显不同的是在glEvalMesh2()中,mode参数可以是GL_POINT、GL_LlINE之外的GL_FILL,它产生用quad-mesh图元填充的多边形。确切的说,glEvalMesh2()与下面的代码片断基本等价。(基本等价,因为当i等于从nujnv,那么参数确切地等于u2v2,而不等于u1+nu*(u2-u1)/nu,这有点可能引起舍入错误)。

glBegin(GL_POINTS); /* mode == GL_POINT */ 
for (i = nu1; i <= nu2; i++) 
for (j = nv1; j <= nv2; j++) 
glEvalCoord2(u1 + i*(u2-u1)/nu, v1+j*(v2-v1)/nv);
glEnd();

or

for (i = nu1; i <= nu2; i++) { /* mode == GL_LINE */
glBegin(GL_LINES); 
for (j = nv1; j <= nv2; j++) 
glEvalCoord2(u1 + i*(u2-u1)/nu, v1+j*(v2-v1)/nv); 
glEnd(); 

for (j = nv1; j <= nv2; j++) { 
glBegin(GL_LINES); 
for (i = nu1; i <= nu2; i++) 
glEvalCoord2(u1 + i*(u2-u1)/nu, v1+j*(v2-v1)/nv); 
glEnd(); 
}

or

for (i = nu1; i < nu2; i++) { /* mode == GL_FILL */ 
glBegin(GL_QUAD_STRIP);
for (j = nv1; j <= nv2; j++) { 
glEvalCoord2(u1 + i*(u2-u1)/nu, v1+j*(v2-v1)/nv);
glEvalCoord2(u1 + (i+1)*(u2-u1)/nu, v1+j*(v2-v1)/nv);
glEnd(); 
}

例子Example 11-3展示了绘制一个贝泽尔曲面时与Example 11-2的必需的不同之处,仅仅是使用glMapGrid2()glEvalMesh2()分割值域平面为一个统一的8x8网格。这个程序也加入了灯光和阴影,如Figuer 11-3所示。

bezmesh.gif (2995 bytes)
Figure 11-3

Example 11-3 : Drawing a Lit, Shaded B閦ier Surface Using a Mesh: bezmesh.c

void initlights(void)
{
GLfloat ambient[] = { 0.2, 0.2, 0.2, 1.0 };
GLfloat position[] = { 0.0, 0.0, 2.0, 1.0 };
GLfloat mat_diffuse[] = { 0.6, 0.6, 0.6, 1.0 };
GLfloat mat_specular[] = { 1.0, 1.0, 1.0, 1.0 };
GLfloat mat_shininess[] = { 50.0 };

glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);

glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
glLightfv(GL_LIGHT0, GL_POSITION, position);
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, mat_diffuse);
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, mat_specular);
glMaterialfv(GL_FRONT_AND_BACK,GL_SHININESS, mat_shininess);
}

void display(void)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glPushMatrix();
glRotatef(85.0, 1.0, 1.0, 1.0);
glEvalMesh2(GL_FILL, 0, 8, 0, 8);
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);
glMapGrid2f(8, 0.0, 1.0, 8, 0.0, 1.0);
initlights();

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值