先了解一下OpenGL中对数据类型的定义,对后面使用一些库函数会有所帮助的。
打开gl.h文件,就可以看到OpenGL定义的一些基本数据类型,如下所示:
typedef unsigned int GLenum;
typedef unsigned char GLboolean;
typedef unsigned int GLbitfield;
typedef signed char GLbyte;
typedef short GLshort;
typedef int GLint;
typedef int GLsizei;
typedef unsigned char GLubyte;
typedef unsigned short GLushort;
typedef unsigned int GLuint;
typedef float GLfloat;
typedef float GLclampf;
typedef double GLdouble;
typedef double GLclampd;
typedef void GLvoid;
先从最简单的学习。
点是OpenGL中最基本最简单的图元,它不像数学中的点是要无穷小的,它是有大小的,大小默认为1个像素,但也可以改变。
改变一个点的大小,函数名称为glPointSize,其函数声明如下:
WINGDIAPI void APIENTRY glPointSize (GLfloat size);
你仍然可以到gl.h中查看该函数的声明。
函数声明中,size是点的大小,默认值为1.0f,单位为“像素”,而且,size必须要大于0.0f,原因很简单了,如果等于0了,你又怎么能在图形显示设备上看到点的存在呢。
为了学习方便,使用VC 6.0进行调试学习。
首先,新建一个Win32 Console Application,切换到Fileview视图,在Source Files中新建一个C++源文件,然后就可以在这个源文件中进行调试学习。
第一个简单的程序如下所示:
#include <windows.h>
#include <GL/glut.h>
void drawPoint(void) {
glClear(GL_COLOR_BUFFER_BIT);
glPointSize(5.0f);
glBegin(GL_POINTS);
glVertex2f(0.0f,0.0f);
glEnd();
glFlush();
}
int main(int argc, char *argv[]) {
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);
glutInitWindowPosition(100,100);
glutInitWindowSize(400,400);
glutCreateWindow("OpenGL基本图元:点");
glutDisplayFunc(&drawPoint);
glutMainLoop();
return 0;
}
编译并运行,可以看到黑色的窗口中显示了一个白色点的效果:

由于初学,对上面的程序作一些必要的解释。
这种以glut开头的函数都是OpenGL的GLUT工具包所提供的函数。
下面对程序中用到的一些函数(库函数和自定义函数)进行说明:
一、自定义void drawPoint(void)函数
这个是我们自己编写的函数,需要在main()函数中调用,从而执行函数定义的功能。 函数的功能就是在窗口中画一个点,点的坐标是(0.0f,0.0f),即窗口的中心位置处;点的大小为5.f个像素。
函数中,首先调用库函数glClear(),该函数的声明如下:
WINGDIAPI void APIENTRY glClear (GLbitfield mask);
函数接受一个GLbitfield类型参数;函数的功能是清除指定的缓存。
这里需要说明的是,函数的参数可以是一个线性组合,含义是可以一次清除多个缓存,因为在图形渲染的过程中,可能存在多个缓存,根据需要进行设置。
例如,清除颜色缓存和深度缓存,可以使用下面的调用:
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
函数参数mask可以是如下值:
GL_COLOR_BUFFER_BIT 颜色缓存
GL_DEPTH_BUFFER_BIT 深度缓存
GL_ACCUM_BUFFER_BIT 累加缓存
GL_STENCIL_BUFFER_BIT 模板缓存
然后,调用glPointSize()函数,设置点的大小,函数声明如下:
WINGDIAPI void APIENTRY glPointSize (GLfloat size);
接着,开始执行画点的操作了。
在OpenGL中进行画图的时候,必须通过使用启用/关闭模式来实现。也就是说,在画图之前要打开,在画图完毕后要关闭,正好对应了glBegin()和glEnd()两个函数:
glBegin()函数的声明如下所示:
WINGDIAPI void APIENTRY glBegin (GLenum mode);
函数参数指定了一种绘图模式mode,具体取值可以是下面之一:
CL_POINTS 一系列独立的点
CL_LINES 每两点相连成为险段
CL_POLYGON 简单、凸多边形的边界
CL_TRIANGLES 三点相连为一个三角形
CL_QUADS 四点相连为一个四边形
CL_LINE_STRIP 顶点相连为一折线
CL_LINE_LOOP 顶点相连为一折线,并将最后一点与第一点相连
CL_TRIANGLE_STRIP 相连的三角形带
CL_TRIANGLE_FAN 相连的三角形扇形
CL_QUAD_STRIP 相连的四边形带
函数glEnd()的声明如下所示:
WINGDIAPI void APIENTRY glEnd (void);
画图只能选择一种模式,可以在glBegin()函数与glEnd()函数之间进行操作。
在glBegin()函数与glEnd()函数之间,调用画点函数glVertex2f(),其声明如下所示:
WINGDIAPI void APIENTRY glVertex2f (GLfloat x, GLfloat y);
函数指定了一个二维坐标(x,y),要在该坐标处进行画点操作。另外,OpenGL库还提供了更多的画点的函数,如下所示:
WINGDIAPI void APIENTRY glVertex2d (GLdouble x, GLdouble y);
WINGDIAPI void APIENTRY glVertex2dv (const GLdouble *v);
WINGDIAPI void APIENTRY glVertex2f (GLfloat x, GLfloat y);
WINGDIAPI void APIENTRY glVertex2fv (const GLfloat *v);
WINGDIAPI void APIENTRY glVertex2i (GLint x, GLint y);
WINGDIAPI void APIENTRY glVertex2iv (const GLint *v);
WINGDIAPI void APIENTRY glVertex2s (GLshort x, GLshort y);
WINGDIAPI void APIENTRY glVertex2sv (const GLshort *v);
WINGDIAPI void APIENTRY glVertex3d (GLdouble x, GLdouble y, GLdouble z);
WINGDIAPI void APIENTRY glVertex3dv (const GLdouble *v);
WINGDIAPI void APIENTRY glVertex3f (GLfloat x, GLfloat y, GLfloat z);
WINGDIAPI void APIENTRY glVertex3fv (const GLfloat *v);
WINGDIAPI void APIENTRY glVertex3i (GLint x, GLint y, GLint z);
WINGDIAPI void APIENTRY glVertex3iv (const GLint *v);
WINGDIAPI void APIENTRY glVertex3s (GLshort x, GLshort y, GLshort z);
WINGDIAPI void APIENTRY glVertex3sv (const GLshort *v);
WINGDIAPI void APIENTRY glVertex4d (GLdouble x, GLdouble y, GLdouble z, GLdouble w);
WINGDIAPI void APIENTRY glVertex4dv (const GLdouble *v);
WINGDIAPI void APIENTRY glVertex4f (GLfloat x, GLfloat y, GLfloat z, GLfloat w);
WINGDIAPI void APIENTRY glVertex4fv (const GLfloat *v);
WINGDIAPI void APIENTRY glVertex4i (GLint x, GLint y, GLint z, GLint w);
WINGDIAPI void APIENTRY glVertex4iv (const GLint *v);
WINGDIAPI void APIENTRY glVertex4s (GLshort x, GLshort y, GLshort z, GLshort w);
WINGDIAPI void APIENTRY glVertex4sv (const GLshort *v);
WINGDIAPI void APIENTRY glVertexPointer (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer);
有必要对这些函数名称的格式进行一下说明:
数字表示参数的个数,字母表示参数的类型,
s 表示16位整数(OpenGL中将这个类型定义为GLshort),
i 表示32位整数(OpenGL中将这个类型定义为GLint和GLsizei),
f 表示32位浮点数(OpenGL中将这个类型定义为GLfloat和GLclampf),
d 表示64位浮点数(OpenGL中将这个类型定义为GLdouble和GLclampd)。
v 表示传递的几个参数将使用指针的方式。
这些函数除了参数的类型和个数不同以外,功能是相同的。
例如,我们要实现一个画点功能例子:画出9个点,void showPoint(void)函数实现如下所示:
void drawPoint(void) {
glClear(GL_COLOR_BUFFER_BIT);
glPointSize(5.0f);
glBegin(GL_POINTS);
glVertex2f(-0.5f,0.5f);
glVertex2f(0.0f,0.5f);
glVertex2f(0.5f,0.5f);
glVertex2f(-0.5f,0.0f);
glVertex2f(0.0f,0.0f);
glVertex2f(0.5f,0.0f);
glVertex2f(-0.5f,-0.5f);
glVertex2f(0.0f,-0.5f);
glVertex2f(0.5f,-0.5f);
glEnd();
glFlush();
}
效果如下所示:

同样,我们可以把这9个点放在一个数组中,然后调用WINGDIAPI void APIENTRY glVertex2fv (const GLfloat *v);函数,如下所示:
void drawPoint(void) {
glClear(GL_COLOR_BUFFER_BIT);
glPointSize(5.0f);
const GLfloat vertex[9][2] = {
{-0.5f,0.5f},
{0.0f,0.5f},
{0.5f,0.5f},
{-0.5f,0.0f},
{0.0f,0.0f},
{0.5f,0.0f},
{-0.5f,-0.5f},
{0.0f,-0.5f},
{0.5f,-0.5f}
};
glBegin(GL_POINTS);
for(int i=0;i<9;i++) {
glVertex2fv(vertex[i]);
}
glEnd();
glFlush();
}
先构造了一个二维数组vertex,然后通过for循环,迭代出每个点的坐标,填充glVertex2fv()的参数即可以画出9个点,效果同上面相同。
最后,调用了glFlush(),将执行该函数调用之前的所有命令,即将所绘制的图形呈现在图形设备上。如果你没有调用它,就不会看到在在glBegin()函数与glEnd()函数之间进行的绘图操作的执行效果。
二、int main(int argc, char *argv[])
主函数中,主要是完成初始化和图形显示的任务。主函数中调用到的函数说明如下:
第一个:函数glutInit()进行GLUT库的初始化工作,它位于glut.h文件中,声明如下所示:
GLUTAPI void APIENTRY glutInit(int *argcp, char **argv);
函数的两个参数对应于入口主函数的两个参数,只是第一个参数用了指针来传递地址。
第二个:函数glutInitDisplayMode()用来设置GLUT库初始化显示模式,声明如下所示:
GLUTAPI void APIENTRY glutInitDisplayMode(unsigned int mode);
参数mode可以指定如下参数:
GLUT_RGB (表示使用RGB颜色)
GLUT_INDEX(表示使用索引颜色)
GLUT_SINGLE(表示使用单缓冲)
GLUT_DOUBLE(使用双缓冲)
第三个:函数glutInitWindowPosition()设置图形在窗口中的显示位置,其声明如下所示:
GLUTAPI void APIENTRY glutInitWindowPosition(int x, int y);
参数指定了一个坐标(x,y)。
第四个:函数glutInitWindowSize()设置显示窗口的尺寸,其声明如下所示:
GLUTAPI void APIENTRY glutInitWindowSize(int width, int height);
参数指定了一对值,表示显示窗口的宽度和高度,单位为像素。
第五个:函数glutCreateWindow()创建显示窗口的标题,其声明如下所示:
GLUTAPI int APIENTRY glutCreateWindow(const char *title);
参数指定了一个字符串,表示窗口的标题。
第六个:函数glutDisplayFunc()根据指定的回调函数,当进行显示的时候执行回调函数。其声明如下所示:
GLUTAPI void APIENTRY glutDisplayFunc(void (GLUTCALLBACK *func)(void));
可以将我们自定义的画图操作的函数的地址传递进来,执行画图操作。
第七个:函数glutMainLoop()进行一个消息循环,窗口被创建后,并不立即显示到屏幕上。需要调用glutMainLoop才能看到窗口。其声明如下所示:
GLUTAPI void APIENTRY glutMainLoop(void);
三、补充知识
RGBA颜色模型:
例如,函数void glClearColor(GLfloat r,GLfloat g,GLfloat b,GLfloat a)用于设置指定窗口的清除颜色,这里的参数使用的就是RGBA颜色模型,其中r,g,b用于设置RGB颜色模型的三个分量,a为透明度设置。这里r、g、b、a的取值范围是0.0~1.0。
绘图模式的开启/关闭:
OpenGL要求,指定顶点的命令必须包含在glBegin函数之后,glEnd函数之前(否则指定的顶点将被忽略)。并由glBegin来指明如何使用这些点。
在glBegin和glEnd之间,只有以下函数是有效的,其它函数全部会被忽略:
glVertex*
glColor*
glIndex*
glSecondaryColor*
glNormal*
glMaterial*
glFogCood*
glTexCood*
glMultiTexCood*
glEdgeFlag*
glArrayElement*
glEvalCoord*
glEvalPoint*
glCallList
glCallLists
其中*表示所有可能的命令格式,比如:glVertex3f(),glVertex2d()等等。