您的位置:首页 > 编程语言

OpenGL编程轻松入门(四)

2006-07-26 01:00 387 查看
1. 曲面和曲线
前面我们讲了如何绘制平面的图形,这一节我们学习如何绘制曲线和曲面。
例10:绘制一个曲面,本程序使用二维求值器绘制一个曲面。本例中也有一些特殊效果的操作。
#include <windows.h>
#include <GL/GLAUX.h>
#include <GL/glut.h>
#include <math.h>

GLfloat ctrlpoints[5][5][3] = {{{-2,0,0},{-1,1,0},{0,0,0},{1,-1,0},{2,0,0}},
{{-2,0,-1},{-1,1,-1},{0,0,-1},{1,-1,-1},{2,0,-1}},
{{-2,0,-2},{-1,1,-2},{0,0,-2},{1,-1,-2},{2,0,-2}},
{{-2,0,-3},{-1,1,-3},{0,0,-3},{1,-1,-3},{2,0,-3}},
{{-2,0,-4},{-1,1,-4},{0,0,-4},{1,-1,-4},{2,0,-4}}};

GLfloat mat_ambient[] = {0.1,0.1,0.1,1.0};
GLfloat mat_diffuse[] = {1.0,0.6,0.0,1.0};
GLfloat mat_specular[] = {1.0,1.0,1.0,1.0};

GLfloat light_ambient[] = {0.1,0.1,0.1,1.0};
GLfloat light_diffuse[] = {1.0,1.0,1.0,0.0};
GLfloat light_specular[] = {1.0,1.0,1.0,0.0};
GLfloat light_position[] = {2.0,23.0,-4.0,1.0};

void myInit(void)
{
glClearColor(0.0,0.0,0.0,0.0);//设置背景色

/*为光照模型指定材质参数*/
glMaterialfv(GL_FRONT,GL_AMBIENT,mat_ambient);
glMaterialfv(GL_FRONT,GL_DIFFUSE,mat_diffuse);
glMaterialfv(GL_FRONT,GL_SPECULAR,mat_specular);
glMaterialf(GL_FRONT,GL_SHININESS,60.0);

/*设置光源参数*/
glLightfv(GL_LIGHT0,GL_AMBIENT,light_ambient);
glLightfv(GL_LIGHT0,GL_DIFFUSE,light_diffuse);
glLightfv(GL_LIGHT0,GL_SPECULAR,light_specular);
glLightfv(GL_LIGHT0,GL_POSITION,light_position);

glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);

/*enable depth comparisons and update the depth buffer*/
glEnable(GL_DEPTH_TEST);
/*设置特殊效果*/
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
glHint(GL_LINE_SMOOTH_HINT,GL_DONT_CARE);
glEnable(GL_BLEND);

glEnable(GL_AUTO_NORMAL);
glEnable(GL_NORMALIZE);
glFrontFace(GL_CW);
glShadeModel(GL_SMOOTH);
glEnable(GL_LINE_SMOOTH);
}

void myDisplay(void)
{
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
glColor3f(0.0,0.0,0.0);
glTranslatef(0.0,-1.0,0.0);
glRotatef(50.0,1.0,0.0,0.0);
glPushMatrix();
/*绘制曲面*/
glEnable(GL_MAP2_VERTEX_3);
glMap2f(GL_MAP2_VERTEX_3,0,1,3,5,0,1,15,5,&ctrlpoints[0][0][0]);
glMapGrid2f(10.0,0.0,1.0,10.0,0.0,1.0);
glEvalMesh2(GL_FILL,0,10.0,0,10.0);
glPopMatrix();
glutSwapBuffers();
}

void myReshape(GLsizei w,GLsizei h)
{
glViewport(0,0,w,h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(60.0,(GLfloat)w/(GLfloat)h,1.0,100.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0.0,0.0,-5.0);
}

int main(int argc,char ** argv)
{
/*初始化*/
glutInit(&argc,argv);
glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGB|GLUT_DEPTH);
glutInitWindowSize(400,400);
glutInitWindowPosition(200,200);

/*创建窗口*/
glutCreateWindow("lighted Bezier surface");

/*绘制与显示*/
myInit();
glutReshapeFunc(myReshape);
glutDisplayFunc(myDisplay);

glutMainLoop();
return(0);
}

myInit()中的几个有关特殊效果的操作。
l glBlendFunc(GLenum sfactor,GLenum dfactor) 指定像素算法。sfactor指定红,绿,蓝及alpha源混合因素是如何计算的。dfactor指定红,绿,蓝及alpha目标混合因素是如何计算的。
l glHint(GLenum target,GLenum mode)指定操作线索。
Target为需要控制的符号常量。mode为所希望的行为符号常数。本例中GL_LINE_SMOOTH_HINT指定反走样线段的采样质量。GL_DONT_CARE指对选项不做考虑。
myDisplay()中的曲面操作
l void glMap2f(GLenum target,GLfloat u1,GLfloat u2,Glint ustride, Glint uorder, GLfloat v1, GLfloat v2, Glint vstride, Glint vorder, const GLfloat *points);定义2维求值器。
target指定求值器生成的数值类型。本例中的GL_MAP2_VERTEX_3 指明每一个控制点为x、y、z表示的三个浮点值。
u1,u2指定线性映射。
ustride 指定控制点Rij的起始点和控制点 R(i+1)j的的起始点之间单精度或双精度浮点值的个数。这里i和j分别是u和v控制点索引,它允许控制点装入任意的数据结构中。唯一的限制是对于特定控制点的数值必须存在连续的内存单元。
uorder控制点数组在u轴方向上的维数。
v1,v2指定线性映射v
vstride指定控制点Rij的起始点和控制点 Ri(j+ 1)的起始点之间单精度或双精度浮点值的个数。这里i和j分别是u和v控制点索引,它允许控制点装入任意的数据结构中。唯一的限制是对于特定控制点的数值必须存在连续的内存单元。
vorder控制点数组在v轴方向上的维数。
points 一个指向控制点数组的指针。
l glMapGrid定义一维或二维网格。void glMapGrid2f(Glint un, GLfloat u1, GLfloat u2,Glint vn, GLfloat v1,GLfloat v2);
un 在网格[u1,u2]中的分段数目。
u1,u2 指定整数网格范围 i= 0;i= un的映射。
vn在网格[v1,v2]中的分段数目。
v1,v2 指定整数网格范围 j = 0;j= vn的映射。
l glEvalMesh 计算一维或二维点或线网格。本例中为2维。void glEvalMesh2(GLenum mode,Glint i1,Glint i2,Glint j1,Glint j2);
mode 指定是否计算二维点、线或多边形的网格。
i1,i2 分别为网格定义域变量i的第一个和最后一个整数值。
j1,j2分别为网格定义域变量j的第一个和最后一个整数值。
glMapGrid和glEvalMesh用来生成并求取一系列等间隔的网格点,glEvalMesh逐步计算一维或二维网格,他的定义范围由glMap指定。mode决定最终计算的顶点是绘制为点、线还是充实的多边形。具体的映射关系及有关图形方面的知识,你可以很方便的在MSDN、互联网及有关书籍中查到,本文就不详述这方面的内容。
2. 绘制NURBS曲线和曲面
上一节讲了一般的曲线与曲面的绘制,本节讲NURBS曲线和曲面的绘制。
例11:此例绘制两个相同形状的NURBS曲面,不同之处是一个为线框式,一个是由实多边形组成。运行后可以看到其中的区别。
#include <windows.h>
#include <GL/glut.h>

GLUnurbsObj *theNurb1;
GLUnurbsObj *theNurb2;

GLfloat ctrlpoints[5][5][3] = {{{-3,0.5,0},{-1,1.5,0},{-2,2,0},{1,-1,0},{-5,0,0}},
{{-3,0.5,-1},{-1,1.5,-1},{-2,2,-1},{1,-1,-1},{-5,0,-1}},
{{-3,0.5,-2},{-1,1.5,-2},{-2,2,-2},{1,-1,-2},{-5,0,-2}},
{{-3,0.5,-3},{-1,1.5,-3},{-2,2,-3},{1,-1,-3},{-5,0,-3}},
{{-3,0.5,-4},{-1,1.5,-4},{-2,2,-4},{1,-1,-4},{-5,0,-4}}};//控制点

GLfloat mat_diffuse[] = {1.0,0.5,0.1,1.0};
GLfloat mat_specular[] = {1.0,1.0,1.0,1.0};
GLfloat mat_shininess[] = {100.0};
GLfloat light_position[] = {0.0,-10.0,0.0,1.0};

void myInit(void)
{

glClearColor(1.0,1.0,1.0,0.0);//设置背景色

/*为光照模型指定材质参数*/
glMaterialfv(GL_FRONT,GL_DIFFUSE,mat_diffuse);
glMaterialfv(GL_FRONT,GL_SPECULAR,mat_specular);
glMaterialfv(GL_FRONT,GL_SHININESS,mat_shininess);

glLightfv(GL_FRONT,GL_POSITION,light_position);//设置光源参数
glLightModeli(GL_LIGHT_MODEL_TWO_SIDE,GL_TRUE);//设置光照模型参数

/*激活光照*/
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);

glDepthFunc(GL_LEQUAL);
glEnable(GL_DEPTH_TEST);
glEnable(GL_LEQUAL);
glEnable(GL_AUTO_NORMAL);
glEnable(GL_NORMALIZE);

/*设置特殊效果*/
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
glHint(GL_LINE_SMOOTH_HINT,GL_DONT_CARE);
glEnable(GL_BLEND);

glFrontFace(GL_CW);
glShadeModel(GL_SMOOTH);
glEnable(GL_LINE_SMOOTH);

theNurb1 = gluNewNurbsRenderer();//创建NURBS对象theNurb1
gluNurbsProperty(theNurb1,GLU_SAMPLING_TOLERANCE,25.0);
gluNurbsProperty(theNurb1,GLU_DISPLAY_MODE,GLU_OUTLINE_POLYGON);

theNurb2 = gluNewNurbsRenderer();//创建NURBS对象theNurb2
gluNurbsProperty(theNurb2,GLU_SAMPLING_TOLERANCE,25.0);
gluNurbsProperty(theNurb2,GLU_DISPLAY_MODE,GLU_FILL);
}

int spin = 0;

/*接收键盘指令*/
static void myKey(unsigned char key,int x,int y)
{
switch(key)
{
case'd':
spin = spin + 1;
glRotatef(spin,1.0,1.0,0.0);
glutPostRedisplay();
break;

case 27:
exit(0);
default:
break;
}
}

/*绘制曲面*/
void myDisplay(void)
{
GLfloat knots[10] = {0.0,0.0,0.0,0.0,0.0,1.0,1.0,1.0,1.0,1.0};

glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
glRotatef(50.0,1.0,1.0,0.0);

/*第一个曲面*/
glPushMatrix();
glTranslatef(1.0,0.0,0.0);
gluBeginSurface(theNurb1);
/*定义曲面形状*/
gluNurbsSurface(theNurb1,10,knots,10,knots,5*3,3,&ctrlpoints[0][0][0],5,5,GL_MAP2_VERTEX_3);
gluEndSurface(theNurb1);
glPopMatrix();

/*第二个曲面*/
glPushMatrix();
glTranslatef(7.0,0.0,0.0);
gluBeginSurface(theNurb2);
/*定义曲面形状*/
gluNurbsSurface(theNurb2,10,knots,10,knots,5*3,3,&ctrlpoints[0][0][0],5,5,GL_MAP2_VERTEX_3);
gluEndSurface(theNurb2);
glPopMatrix();

glutSwapBuffers();
}

void myReshape(GLsizei w,GLsizei h)
{
glViewport(0,0,w,h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(50.0,(GLfloat)w/(GLfloat)h,1.0,15.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0.0,0.0,-9.0);
}

int main(int argc,char ** argv)
{
/*初始化*/
glutInit(&argc,argv);
glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGB|GLUT_DEPTH);
glutInitWindowSize(600,400);
glutInitWindowPosition(200,200);

/*创建窗口*/
glutCreateWindow("NURBS surface");

/*绘制与显示*/
myInit();
glutKeyboardFunc(myKey);
glutReshapeFunc(myReshape);
glutDisplayFunc(myDisplay);

glutMainLoop();
return(0);
}
l GLUnurbsObj* glNewNurbsRenderer()创建一个NURBS对象,并返回一个指向该对象的指针。如果没有足够的内存分配给该对象,则返回值为0。
l void gluNurbsProperty(GLUnurbsObj* nobj, GLenum property, GLfloat value)设置NURBS属性。
nobj 指向NURBS对象的指针。
property需设置的属性。
value 设置指定属性的值。
l glBeginSurface及gluEndSurface两个函数一起限定一个NURBS面的定义。返回值均为void,参数均为GLUnurbsObj* nobj,为指向NURBS对象的指针。
l void gluNurbsSurface(GLUnurbsObj *nobj, Glint knot_count, GLfloat tknot_count, GLfloat *tknot, Glint s_stride, Glint t_stride, GLfloat *ctlarry, GLint sorder, GLint torder,GLenum type) 定义NURBS曲面形状。
nobj 指向NURBS对象的指针。
sknot_count 参数化u方向上的节点数。
sknot 参数化u方向上的非递减节点值。
tknot_count 参数化v方向上的节点数。
tknot 参数化v方向上的非递减节点值。
s_stride在ctlarry中参数化u方向上相邻控制点的偏移量。
t_stride在ctlarry中参数化v方向上相邻控制点的偏移量。
ctlarryNURBS的控制点数组。
sorder参数化u方向上NURBS的阶数,阶数比维数大1。
torder参数化v方向上NURBS的阶数,阶数比维数大1。
type曲面类型。
例12:绘制一个彩色的曲线,曲线闭合成圆。在曲线的边缘绘制8个点。
#include <windows.h>
#include <GL/glut.h>

GLUnurbsObj *theNurb;

GLfloat ctrlpoints[12][3] = {{4,0,0},{2.828,2.828,0},{0,4,0},{-2.828,2.828,0},
{-4,0,0},{-2.828,-2.828,0},{0,-4,0},{2.828,-2.828,0},
{4,0,0},{2.828,2.828,0},{0,4,0},{2.828,2.828,0}};//控制点

GLfloat color[12][3]={{1.0,0.0,0.0},{1.0,1.0,0.0},{0.0,1.0,0.0},{-1.0,1.0,0.0},
{-1.0,0.0,0.0},{-1.0,-1.0,0.0},{0.0,-1.0,0.0},{1.0,-1.0,0.0},
{1.0,0.0,0.0},{1.0,1.0,0.0},{0.0,1.0,0.0},{1.0,1.0,0.0}};

GLfloat knots[15] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};

void myInit(void)
{

glClearColor(1.0,1.0,1.0,0.0);//设置背景色

theNurb = gluNewNurbsRenderer();//创建NURBS对象theNurb
gluNurbsProperty(theNurb,GLU_SAMPLING_TOLERANCE,10);

}

/*绘制曲线*/
void myDisplay(void)
{
int i;

glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
glColor3f(0.0,0.0,0.0);

glLineWidth(3.0);

/*绘制曲线*/
gluBeginCurve(theNurb);
gluNurbsCurve(theNurb,15,knots,3,&ctrlpoints[0][0],3,GL_MAP1_VERTEX_3);
gluNurbsCurve(theNurb,15,knots,3,&ctrlpoints[0][0],3,GL_MAP1_COLOR_4);
gluEndCurve(theNurb);

/*绘制点*/
glColor3f(1.0,0.0,0.0);
glPointSize(5.0);
glBegin(GL_POINTS);
for(i = 0;i < 8;i++)
glVertex2fv(&ctrlpoints[i][0]);
glEnd();

glutSwapBuffers();
}

void myReshape(GLsizei w,GLsizei h)
{
glViewport(0,0,w,h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();

if(w <=h)
glOrtho(-10.0,10.0,-10.0*(GLfloat)h/(GLfloat)w,10.0*(GLfloat)h/(GLfloat)w,-10.0,10.0);
else
glOrtho(-10.0*(GLfloat)w/(GLfloat)h,10.0*(GLfloat)w/(GLfloat)h,-10.0,10.0,-10.0,10.0);

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0.0,0.0,-9.0);
}

int main(int argc,char ** argv)
{
/*初始化*/
glutInit(&argc,argv);
glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGB|GLUT_DEPTH);
glutInitWindowSize(600,400);
glutInitWindowPosition(200,200);

/*创建窗口*/
glutCreateWindow("NURBS curve");

/*绘制与显示*/
myInit();
glutReshapeFunc(myReshape);
glutDisplayFunc(myDisplay);

glutMainLoop();
return(0);
}
l gluBeginCurve,gluEndCurve限定NURBS曲面。返回值均为void,参数均为GLUnurbsObj* nobj,为指向NURBS对象的指针。
l void gluNurbsCurve(GLUnurbsObj *nobj, GLint nknots, GLfloat *knot, Glint stride, GLfloat *ctlarray, GLint order,GLenum type)定义曲线形状。
nobj 指向NURBS对象的指针。
nknots 节点数,节点数等于控制点数加上阶数。
knot nknots数组非递减节点值。
stride相邻控制点的偏移量。
Ctlarry指向NURBS的控制点数组的指针。
order NURBS曲线的阶数,阶数比维数大1。
type曲面类型。
3. 二次几何体
这一章我们讲一下二次几何物体的绘制。二次几何物体的绘制有几种不同的方式,在本例中可以看出不同的绘制方式的不同效果。
例13:本例使用GLU库函数绘制了四个几何物体,分别为圆柱体、球体、圆盘和部分圆盘。
#include <windows.h>
#include <GL/glut.h>
#include <math.h>

/*声明四个二次曲面物体*/
GLUquadricObj *quadObj1;
GLUquadricObj *quadObj2;
GLUquadricObj *quadObj3;
GLUquadricObj *quadObj4;

static float light_ambient[] = {0.1,0.1,0.1,1.0};
static float light_diffuse[] = {0.5,1.0,1.0,1.0};
static float light_position[] = {90.0,90.0,150.0,0.0};

static float front_mat_shininess[] = {60.0};
static float front_mat_specular[] = {0.2,0.2,0.2,1.0};
static float front_mat_diffuse[] = {0.5,0.5,0.28,1.0};

static float back_mat_shininess[] = {60.0};
static float back_mat_specular[] = {0.5,0.5,0.2,1.0};
static float back_mat_diffuse[] = {1.0,0.9,0.2,1.0};

static float Imodel_ambient[] = {1.0,1.0,1.0,1.0};
static float Imodel_twoside[] = {GL_TRUE};
static float Imodel_oneside[] = {GL_FALSE};

void myInit(void)
{
/*设置背景色*/
glClearColor(0.0,0.0,0.0,1.0);

glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);

/*设置光照*/
glLightfv(GL_LIGHT0,GL_AMBIENT,light_ambient);
glLightfv(GL_LIGHT0,GL_DIFFUSE,light_diffuse);
glLightfv(GL_LIGHT0,GL_POSITION,light_position);

/*设置材质*/
glMaterialfv(GL_FRONT,GL_DIFFUSE,front_mat_diffuse);
glMaterialfv(GL_FRONT,GL_SPECULAR,front_mat_specular);
glMaterialfv(GL_FRONT,GL_SHININESS,front_mat_shininess);

glMaterialfv(GL_BACK,GL_DIFFUSE,back_mat_diffuse);
glMaterialfv(GL_BACK,GL_SPECULAR,back_mat_specular);
glMaterialfv(GL_BACK,GL_SHININESS,back_mat_shininess);

/*设置光照模型参数*/
glLightModelfv(GL_LIGHT_MODEL_AMBIENT,Imodel_ambient);
glLightModelfv(GL_LIGHT_MODEL_TWO_SIDE,Imodel_twoside);

/*激活关照*/
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glShadeModel(GL_SMOOTH);
}

void myDisplay(void)
{
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

/*创建四个二次曲面物体*/
quadObj1 = gluNewQuadric();
quadObj2 = gluNewQuadric();
quadObj3 = gluNewQuadric();
quadObj4 = gluNewQuadric();

/*绘制一个圆柱体*/
glPushMatrix();
gluQuadricDrawStyle(quadObj1,GLU_FILL);
gluQuadricNormals(quadObj1,GL_FLAT);
gluQuadricOrientation(quadObj1,GLU_INSIDE);
gluQuadricTexture(quadObj1,GL_TRUE);

glColor3f(1.0,1.0,0.0);
glRotatef(30,1.0,0.0,0.0);
glRotatef(40,0.0,1.0,0.0);
gluCylinder(quadObj1,2.0,2.0,9.0,20.0,8.0);
glPopMatrix();

/*绘制一个球体*/
glPushMatrix();
gluQuadricDrawStyle(quadObj2,GLU_SILHOUETTE);
glTranslatef(-5.0,-1.0,0.0);
gluSphere(quadObj2,3.0,20.0,20.0);
glPopMatrix();

/*绘制一个圆盘*/
glPushMatrix();
gluQuadricDrawStyle(quadObj3,GLU_LINE);
glTranslatef(-2.0,4.0,0.0);
gluDisk(quadObj3,2.0,5.0,15.0,10.0);
glPopMatrix();

/*绘制一个部分圆盘*/
glPushMatrix();
gluQuadricDrawStyle(quadObj4,GLU_POINT);
glTranslatef(-3.0,-7.0,0.0);
gluPartialDisk(quadObj4,2.0,5.0,15.0,10.0,10.0,100.0);
glPopMatrix();

/*删除四个二次曲面物体对象*/
gluDeleteQuadric(quadObj1);
gluDeleteQuadric(quadObj2);
gluDeleteQuadric(quadObj3);
gluDeleteQuadric(quadObj4);

glFlush();
}

void myReshape(int w,int h)
{
glViewport(0,0,(GLsizei)w,(GLsizei)h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45.0,(GLfloat)w/(GLfloat)h,1.0,50.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0.0,0.0,-25.0);
}

int main(int argc,char ** argv)
{
/*初始化*/
glutInit(&argc,argv);
glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB|GLUT_DEPTH);
glutInitWindowSize(400,400);
glutInitWindowPosition(100,100);

/*创建窗口*/
glutCreateWindow(" DRAW QUADRIC OBJECTS ");

/*绘制与显示*/
myInit();
glutReshapeFunc(myReshape);
glutDisplayFunc(myDisplay);

glutMainLoop();
return 0;
}
l gluNewQuadric创建一个二次对象。这个函数创建并返回一个指向新的二次对象的指针。当调用二次描述和控制函数是指向这个对象。如果返回值为0则表明没有足够的空间分配给该对象。
l glQuadricDrawStyle函数指定二次对象的绘制方式。本例中圆柱体的绘制方式为GLU_FILL。含义为用多边形原绘制本二次对象,多边形的绘制方式为逆时针。球体的绘制方式为GL_SILHOUETTE,即除边界外用一系列线来绘制二次对象。圆盘的绘制方式为GL_LINE,即用一系列线来绘制二次对象。部分圆盘的绘制方式为GL_POINT,即用一系列点来绘制二次对象。
l glQuadricNormals,指定二次对象使用的法向量类型。
l glQuadricOrientation,指定二次对象的内面或外面方位。GLU_OUTSIDE为缺省值,表明使用指向内面的法相量绘出二次对象,GLU_INSIDE表明使用指向外面的法相量绘出二次对象。
l glQuadricTexture指定二次对象是否使用纹理。GL_FALSE为缺省值。
l void gluCylinder(GLUquadricObj *qobj,GLdouble baseRadius,GLdouble topRadius,GLdouble height,Glint slices,Glint stacks)绘制一个圆柱体。
qobj指明是哪个二次对象。
baseRadius圆柱体在z=0时的半径。
topRadius圆柱体在z=height时的半径。
height圆柱体的高。
slices围绕着z轴分片的个数。
stacks顺着z轴分片的个数。stacks和slices垂直。
l void gluSphere(GLUquadricObj *qobj,GLdouble radius,Glint slices,Glint stacks)绘制一个球形。
qobj指明是哪个二次对象。
radius球体半径。
slices围绕着z轴分片的个数。
stacks顺着z轴分片的个数。
l void gluDisk(GLUquadricObj *qobj,GLdouble innerRadius,GLdouble outerRadius,Glint slices,Glint loops)绘制一个圆盘。
qobj指明是哪个二次对象。
innerRadius圆盘的内部半径,可能为0。
outerRadius圆盘的外部半径。
slices围绕着z轴分片的个数。
loops圆盘同心圆个数。
l void gluPartialDisk(GLUquadricObj *qobj,GLdouble innerRadius,GLdouble outerRadius,Glint slices,Glint loops,GLdouble startAngle,GLdouble sweepAngle)绘制一个圆盘的一部分。
startAngle起始角度,单位为度。
sweepAngle扫描角,单位为度。
4. 像素操作
本节讲解像素操作。
例14:本例在窗口绘制一个三角形,然后利用glCopyPixel函数拷贝了五次该图形,并将这五个三角形放置在窗口的不同位置。
#include <stdlib.h>
#include <GL/glut.h>

/*设置背景色*/
void myInit(void)
{
glClearColor(1.0,1.0,1.0,1.0);
}

/*绘制彩色三角形*/
void triangle(void)
{
glBegin(GL_TRIANGLES);
glColor3f(1.0,1.0,0.0);
glVertex2f(10.0,10.0);
glColor3f(0.0,0.3,1.0);
glVertex2f(20.0,30.0);
glColor3f(1.0,0.0,0.0);
glVertex2f(30.0,10.0);
glEnd();
}

void myDisplay()
{
int i;

glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

glPushMatrix();
glTranslatef(10.0,100.0,0.0);
triangle();
glPopMatrix();

glPushMatrix();
for(i = 0;i < 5;i++)
{
glRasterPos2i(20+i*30,10+i*5);//为像素操作指定光栅位置
glCopyPixels(50,200,500,500,GL_COLOR);//在缓存中拷贝像素
}
glPopMatrix();

glFlush();
}

void myReshape(int w,int h)
{
glViewport(0,0,(GLsizei)w,(GLsizei)h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();

if( w <= h)
gluOrtho2D(0.0,150,0.0,150.0*(GLfloat)h/(GLfloat)w);
else
gluOrtho2D(0.0,150*(GLfloat)w/(GLfloat)h,0.0,150.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}

int main(int argc,char ** argv)
{
glutInit(&argc,argv);
glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB|GLUT_DEPTH);
glutInitWindowSize(500,400);
glutInitWindowPosition(100,100);

glutCreateWindow(" copy ");

myInit();
glutReshapeFunc(myReshape);
glutDisplayFunc(myDisplay);

glutMainLoop();
return 0;
}
l glRasterPos在像素操作中指定光栅位置。同样后面的数字表示坐标维数,2表示坐标为x、y,3表示、,y、z。数字后面的字母表示参数类型。最后带V表示参数为指针。
l void glCopyPixels(GLint x, Glint y, GLsizei width, GLsizei height, GLenum type)函数将像素拷贝到缓存中。
x,y指定被拷贝像素的右下角坐标。
width,heigth指定被拷贝像素矩形区域的大小。
type指定拷贝数值的类型。值为颜色、深度或膜板值。本例中为颜色。

例15:此例在窗口左下角写单词FILE。
#include <stdlib.h>
#include <GL/glut.h>

/*字母F*/
GLubyte f_rasters[12] = {0xc0,0xc0,0xc0,0xc0,0xc0,0xfc,
0xfc,0xc0,0xc0,0xc0,0xff,0xff};

/*字母I*/
GLubyte i_rasters[12] = {0xff,0xff,0x18,0x18,0x18,0x18,
0x18,0x18,0x18,0x18,0xff,0xff};

/*字母L*/
GLubyte l_rasters[12] = {0xff,0xff,0xc0,0xc0,0xc0,0xc0,
0xc0,0xc0,0xc0,0xc0,0xc0,0xc0};

/*字母E*/
GLubyte e_rasters[12] = {0xff,0xff,0xc0,0xc0,0xc0,0xff,
0xff,0xc0,0xc0,0xc0,0xff,0xff};

void myInit(void)
{
glPixelStorei(GL_UNPACK_ALIGNMENT,1);//设置像素存储模式
glClearColor(1.0,1.0,1.0,1.0);//设置背景为白色
}

void myDisplay()
{
glClear(GL_COLOR_BUFFER_BIT);

glColor3f(0.0,0.0,0.0);//设置像素颜色为黑色
glRasterPos2i(20,20);//为像素指定位置

/*绘制位图*/
glBitmap(8,12,0.0,0.0,14.0,0.0,f_rasters);
glBitmap(8,12,0.0,0.0,14.0,0.0,i_rasters);
glBitmap(8,12,0.0,0.0,14.0,0.0,l_rasters);
glBitmap(8,12,0.0,0.0,14.0,0.0,e_rasters);
glFlush();
}

void myReshape(int w,int h)
{
glViewport(0,0,(GLsizei)w,(GLsizei)h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0,w,0,h,-1.0,1.0);

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}

int main(int argc,char ** argv)
{
/*初始化*/
glutInit(&argc,argv);
glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB);
glutInitWindowSize(500,400);
glutInitWindowPosition(100,100);

/*创建窗口*/
glutCreateWindow(" BitMap");

/*绘制图形*/
myInit();
glutReshapeFunc(myReshape);
glutDisplayFunc(myDisplay);

glutMainLoop();//进入GLUT事件处理循环
return 0;
}
l glBitmap(GLsizei width,GLsizei height,GLfloat xorig,GLfloat yorig,GLfloat xmove,GLfloat ymove,const GLubyte *bitmap)函数绘制一个位图。
width,height分别指定位图图像的宽度和高度。
xorig,yorig位图图像的原点位置。原点为位图的左下角。向右和向上为坐标轴的正向。
xmove,ymove绘制完位图后x,y相对于当前光栅的位移。
bitmap位图图像的地址。
你可以改变此函数的参数,观察不同大小不同位置的效果。
5. 动画
到目前为止我们所做的图形全部都是静止的。而OpenGL的是一个可以制作大型3D图形、动画的工具。下面我们做一个可以旋转的立方体。
例16:一个旋转的立方体
#include <GL/glut.h>

GLfloat X = 10.0f;
GLfloat Y = 1.0f;
GLfloat Z = 5.0f;

void myDisplay(void)
{
GLfloat diffuse[] = {0.7,0.7,0.0,1.0};
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
glColor3f(1.0,1.0,1.0);
/*绘制立方体*/
glPushMatrix();
glRotatef(X,1.0,0.0,0.0);
glRotatef(Y,0.0,1.0,0.0);
glRotatef(Z,0.0,0.0,1.0);
glMaterialfv(GL_FRONT,GL_DIFFUSE,diffuse);
glutSolidCube(1.0);
glPopMatrix();
glFlush();
glutSwapBuffers();//交换当前窗口使用层的缓存
}

void myReshape(int w,int h)
{
glViewport(0,0,w,h);

glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(-1.5,1.5,-1.5,1.5);
if(w <= h)
glOrtho(-2.25,2.25,-2.25*h/w,2.25*h/w,-10.0,10.0);
else
glOrtho(-2.25*w/h,2.25*w/h,-2.25,2.25,-10.0,10.0);

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}

void myAnimate(void)
{
X += 1.0; 
Y += 1.0;
Z += 1.0;

glutPostRedisplay();//标记当前窗口需要重新绘制

}

int main(int argc,char ** argv)
{
/*初始化*/
glutInit(&argc,argv);

glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGB|GLUT_DEPTH);
glutInitWindowSize(400,400);
glutInitWindowPosition(200,200);

/*创建窗口*/
glutCreateWindow("color");

/*绘制与显示*/
glutReshapeFunc(myReshape);

glutDisplayFunc(myDisplay);
glutIdleFunc(myAnimate);//设置全局空闲回调函数

glutMainLoop();

return(0);
}
myDisplay函数中有一个glutSwapBuffers()函数。此函数交换当前窗口使用层的缓存,它将后台缓存中的内容交换到前台缓存中,该函数执行的结果直到显示器垂直回行扫描后才看得到。必须使用双缓存结构,否则此函数不起任何作用。
myAanimate函数中glutPostRedisplay()函数标记当前窗口需要重新绘制。在glutMainLoop函数的事件处理循环的下一个反复中,将调用该窗口的显示回调函数重绘该窗口的图像层。
在main函数中glutInitDisplayMode中为GLUT_DOUBLE,而我们以前的很多例子为GLUT_SINGLE。main函数中还调用了glutIdleFunc,此函数设置全局空闲回调函数。,从而使GLUT程序可以执行后台任务或连续动画。
6. 菜单管理
菜单是我们经常使用的工具,很方便,直观。本节讨论在OpenGL中如何进行菜单管理。
例17:本例在蓝色的背景上绘制一个白色的正方形,在窗口内单击鼠标右键,弹出菜单,当选择不同菜单项时命令窗口会显示出是哪个菜单的哪个菜单相被激活。本例一个主菜单,主菜单有3个菜单条目及两个子菜单。两个子菜单都有3个菜单条目。
#include <GL/glut.h>
#include <stdio.h>

int menu,subMenu1,subMenu2;

/*在蓝色的背景上绘制一个白色的正方形*/
void myDraw(void)
{
glClearColor(0.0,0.0,1.0,0.0);
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
glColor3f(1.0,1.0,1.0);
glOrtho(-1.0,1.0,-1.0,1.0,-1.0,1.0);
glBegin(GL_POLYGON);
glVertex2f(-0.5,-0.5);
glVertex2f(-0.5,0.5);
glVertex2f(0.5,0.5);
glVertex2f(0.5,-0.5);
glEnd();
glutSwapBuffers();
}

/*写出当前是哪个菜单*/
void GetCurrentMenu(void)
{
int nMenu;
nMenu = glutGetMenu();
if(nMenu == menu)
printf("The current menu is Main Menu.\n");
if(nMenu == subMenu1)
printf("The current menu is SubMenu1.\n");
if(nMenu == subMenu2)
printf("The current menu is SubMenu2.\n");
}

/*子菜单1*/
void SubMenuFunc1(int data)
{
GetCurrentMenu();
switch(data)
{
case 1:
printf("SubMenu1's item 1 is triggered.\n");
break;
case 2:
printf("SubMenu1's item 2 is triggered.\n");
break;
case 3:
printf("SubMenu1's item 3 is triggered.\n");
break;
}
}

/*子菜单2*/
void SubMenuFunc2(int data)
{
GetCurrentMenu();
switch(data)
{
case 1:
printf("SubMenu2's item 1 is triggered.\n");
break;
case 2:
printf("SubMenu2's item 2 is triggered.\n");
break;
case 3:
printf("SubMenu2's item 3 is triggered.\n");
break;
}
}

/*主菜单*/
void MenuFunc(int data)
{
GetCurrentMenu();
switch(data)
{
case 1:
printf("MainMenu's item 1 is triggered.\n");
break;
case 2:
printf("MainMenu's item 2 is triggered.\n");
break;
case 3:
printf("MainMenu's item 3 is triggered.\n");
break;
}
}

int main(int argc,char ** argv)
{
/*初始化*/
glutInit(&argc,argv);
glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGB|GLUT_DEPTH);
glutInitWindowSize(500,400);
glutInitWindowPosition(100,100);

/*创建窗口*/
glutCreateWindow(" MENU");

glutDisplayFunc(myDraw);

/*创建子菜单1并加入菜单条目*/
subMenu1 = glutCreateMenu(SubMenuFunc1);
glutAddMenuEntry("SubMenu1's item1",1);
glutAddMenuEntry("SubMenu1's item2",2);
glutAddMenuEntry("SubMenu1's item3",3);
glutAttachMenu(GLUT_RIGHT_BUTTON);

/*创建子菜单2并加入菜单条目*/
subMenu2 = glutCreateMenu(SubMenuFunc2);
glutAddMenuEntry("SubMenu2's item1",1);
glutAddMenuEntry("SubMenu2's item2",2);
glutAddMenuEntry("SubMenu2's item3",3);
glutAttachMenu(GLUT_RIGHT_BUTTON);

/*创建主菜单并加入菜单条目及子菜单*/
menu = glutCreateMenu(MenuFunc);
glutAddMenuEntry("Item1",1);
glutAddMenuEntry("Item2",2);
glutAddMenuEntry("Item3",3);
glutAddSubMenu("SubMenu 1",subMenu1);
glutAddSubMenu("SubMenu 2",subMenu2);
glutAttachMenu(GLUT_RIGHT_BUTTON);

glutMainLoop();//进入GLUT事件处理循环
return 0;
}
l int glutCreateMenu(void(*func)(int value))创建一个新的弹出式菜单并返回一个唯一标识此菜单的整型表示符。func指明此菜单的功能。
l int glutAddMenuEntry(char *name,int value)在当前菜单底部增加一个菜单条目。
name指定显示在新菜单条目上的ASCII码字符串。
value指定当选择该菜单条目时传递到菜单回调函数中的数值。
l void glutAddSubMenu(char *name,int menu)在当前菜单的底部增加一个子菜单触发条目。
name指定显示在新菜单触发条目上的ASCII码字符串。
meun当选择该子菜单触发条目时弹出的子菜单的标识符。
l void glutAttachMenu(int button)把当前窗口的一个鼠标按键与当前菜单的标识符联系起来。
button指明鼠标的哪个按键。GLUT_LEFT_BUTTON、GLUT_MIDDLE_BUTTON及GLUT_RIGHT_BUTTON,分别表明鼠标左、中及右键。
l int glutGetMenu(void)获取当前菜单的标识符,如果没有菜单存在或前一个当前菜单被删除了,glutGetMenu则返回0值。
7. 小结
本文介绍了有关OpenGL的基本知识,主要涉及颜色、绘制几何体、坐标变换、堆栈操作、显示列表、光照和材质、纹理映射、特殊效果、曲面和曲线的绘制、二次几何体绘制、像素操作、如何绘制动画物体及菜单管理。OpenGL查询函数、网格化以及互操作等许多方面都未涉及。即使涉及的有关方面也比较浅显。但是通过对本文及本文中例子的理解消化,你可以较容易的掌握OpenGL其他知识点。相信,通过一段时间的学习,你将很快得成为OpenGL的高手。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息