文章目录


OpenGL基本函数库用来描述图元、属性、几何变换、观察变换和进行许多其它的操作。


一、基本的OpenGL语法

OpenGL基本库(也称为OpenGL核心库)中的函数名要以gl为前缀,并且函数名中每一个组成词的第一个字母要大写,例如:

glBegin、glClear、glCopyPixels、glPolygonMode


有些函数要求一个(或多个)变量用符号常量赋值,如参数名、参数的值或特定的模式。所以这些常量均以大写字母GL开头。另外,常量名中的每一个组成词均采用大写,单词之间用下划线(_)分隔开,例如:

GL_2D、GL_RGB、GL_CCW、GL_POLYGON、GL_AMBIENT_AND_DIFFUSE


OpenGL函数也要求专门的数据类型。例如,OpenGL函数的参数可以要求一个32位整数类型的值。但是,不同机器上的整数描述范围可能有所不同。OpenGL采用专门的内置数据类型名来描述数据类型,例如:

GLbyte、GLshort、GLint、GLfloat、GLdouble、GLboolean

每个数据类型名以大写字母GL开头,名字中其余部分是用小写字母表示的标准数据类型名。


OpenGL函数的某些变量可以采用数组赋值,从而列出一组数据的值。这是作为指向数组的指针来指定一组数值而不是作为显示变量指定该组数据中每一个数据的替代方法。指定xyz坐标值就是该方法的典型例子。


二、相关库

除了OpenGL基本库之外,还有一些用于处理专门操作的附加库。

OpenGL实用函数(OpenGL Utility,GLU)提供了一些例程,可以设置观察和投影矩阵,利用线条和多边形近似法来描述复杂对象,使用线性近似法显示二次曲线和样条曲线,处理标准绘制操作,以及完成其他的复杂任务。每一个OpenGL实现中都包括GLU库,所有GLU函数名均用前缀glu开头。还有一个称为Open Inventor的基于OpenGL的面向对象工具包,它为交互式三维应用提供函数和预定义的对象形状。该工具包采用C++编程。

为了使用OpenGL建立一个图形,首先必须在视频屏幕上设置显示窗口,因为OpenGL库中只有与设备无关的函数,且窗口管理操作依赖于所用的计算机,所以基本的OpenGL函数不能够直接创建显示窗口。但是,有多个支持各种计算机上的OpenGL函数的窗口系统库:

  • OpenGL的X窗口系统扩充提供了一组以glx为前缀的函数;
  • Apple系统可使用Apple GL(AGL)接口进行窗口管理操作,该库的函数名以agl为前缀;
  • 对于Windows系统,WGL函数提供了Windows到OpenGL的接口,这些函数以wgl为前缀;
  • Presentation Manager to OpenGL(PGL)是一个用于IBM OS/2的接口,它使用pgl作为库函数的前缀;
  • OpenGL实用函数工具包(OpenGL Utility Toolkit,GLUT)提供了与任意屏幕窗口系统进行交互的函数库,GLUT库函数以glut为前缀;由于GLUT是一个与其他依赖于设备的窗口系统之间的接口,我们可以利用它使得程序称为与设备无关的。

三、头文件

在我们所有的程序中,需要包含一个头文件来引入OpenGL基本库。在许多应用中,我们都需要GLU,并且在许多系统中都需要包含引入窗口系统的头文件,例如对于Windows系统,存取WGL函数的头文件是​​windows.h​​,该头文件必须列在OpenGL和GLU头文件之前,因为它包含了OpenGL库的Windows版本所需的宏,因此,源程序的开头几行是:

#include <windows.h>
#include <GL/gl.h>
#include <GL/glu.h>

然而,如果我们使用GLUT处理窗口管理操作,就不需要引入​​gl.h​​​和​​glu.h​​,因为GLUT保证了它们的正确引入。因此,我们可以使用:

#include <GL/glut.h>

来代替OpenGL和GLUT的头文件。(也可以再次引入​​gl.h​​​和​​glu.h​​,但这将造成冗余且影响了程序的可移植性)


四、使用GLUT进行显示窗口管理

我们从使用简化的、最少的操作来显示一个图开始。使用OpenGL实用库的第一步是初始化GLUT。完成GLUT初始化的语句是:

glutInit(&argc, argv);

接着,需要说明的是显示窗口在创建时要给定一个标题:

glutCreateWindow("An Example OpenGL Program");

下面,我们需要指定显示窗口中药显示什么内容。为此,使用OpenGL函数创建一个图并将图的定义传递给GLUT函数​​glutDisplayFunc​​,即将图赋给显示窗口。例如,我们已经有了线段的OpenGL描述程序lineSegment,则调用下列函数就将线段描述送到显示窗口:

glutDisplayFunc(lineSegment);

但是显示窗口还未出现在屏幕上,我们需要使用下列语句,所有已创建的显示窗口连同其中的图形内容都将被激活:

glutMainLoop();

该函数必须是程序中的最后一个。它显示初始图形并使程序进入检查鼠标或键盘等设备输入的无穷循环之中。因为这个例子不是交互式的,所以程序仅仅显示其中的图形直到显示窗口关闭,后续再介绍如何修改OpenGL程序,使之能处理交互输入。


可以使用​​glutInitWindowPostion​​来设置显示窗口左上角的初始位置,该位置使用以屏幕左上角为原点的整数坐标来表示。例如:

glutInitWindowPosition(50, 100);

可以使用​​glutInitWindowSize​​来设置显示窗口的初始宽度和高度的像素数。例如:

glutInitWindowSize(400, 300);

计算机图形学3--OpenGL简介_头文件


还可以使用​​glutInitDisplayMode​​函数来设定显示窗口的缓存和颜色模型等选项。该函数的变量使用符号化GLUT常量来赋值。例如,下面的命令指出显示窗口使用单个缓存且使用红、绿、蓝(RGB)三元素组成的颜色模型来选择颜色值:

glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);

五、一个完整的OpenGL程序

计算机图形学3--OpenGL简介_开发语言_02

#include <GL/glut.h>

void init() {
glClearColor(1.0, 1.0, 1.0, 0.0); //设置显示窗口背景为白色

glMatrixMode(GL_PROJECTION); //设置投影模式
gluOrtho2D(0.0, 200.0, 0.0, 150.0); //设置观察参数
};

void lineSegment() {
glClear(GL_COLOR_BUFFER_BIT); //清除颜色缓存,使用glClearColor设置的值

glColor3f(0.0, 0.4, 0.2); //使用浮点数设置对象颜色
glBegin(GL_LINES); //开始绘制线段
glVertex2i(180, 15); //设置线段的点
glVertex2i(10, 145);
glEnd(); //结束绘制线段

glFlush();
}

int main(int argc, char** argv) {
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
glutInitWindowSize(400, 300);
glutCreateWindow("An Example OpenGL Program");

init();
glutDisplayFunc(lineSegment);
glutMainLoop();

return 0;
}

1、关于glClearColor(r, g, b, a)和glClear(GLbitfield mask)

  • ​glClearColor​​函数用来设置显示窗口的背景色,但是不是设置了就会马上生效;
  • ​glClear​​​函数是用当前缓冲区清除值,也就是​​glClearColor​​​或者​​glClearDepth​​​、​​glClearIndex​​​、​​glClearStencil​​​、​​glClearAccum​​等函数所指定的值来清除指定的缓冲区;
  • GLbitfield mask可以使用如下OpenGL符号常量:
  • GL_COLOR_BUFFER_BIT: 当前可写的颜色缓冲
  • GL_DEPTH_BUFFER_BIT: 深度缓冲
  • GL_ACCUM_BUFFER_BIT: 累积缓冲
  • GL_STENCIL_BUFFER_BIT: 模板缓冲

2、设置对象颜色
除了设定显示缓存的背景色,还可以为要显示的场景中的对象选择各种颜色,使用glColor3f(0.0, 0.4, 0.2)设置三个RGB颜色分量,glColor函数3f后缀表示使用浮点数;

3、设置投影类型和观察参数

glMatrixMode(GL_PROJECTION);    //设置投影模式
gluOrtho2D(0.0, 200.0, 0.0, 150.0); //设置观察参数

在OpenGL中把生成二维线段看成生成三维线段的特例,所以在OpenGL中显示图形需要将图形投影到窗口中;

glMatrixMode(GL_PROJECTION)表示使用正投影将世界坐标系的二维矩形区域的内容映射到屏幕上;

gluOrtho2D(0.0, 200.0, 0.0, 150.0)设置 观察参数,定义显示窗口以(0.0,0.0)为左下角,以(200.0, 150.0)为右上角;

4、绘制线段
OpenGL中绘制图形通常由​​​glBegin​​​和​​glEnd​​程序段组成,具体绘制什么,由glBegin(图形类型)中的参数决定,程序段中提供待绘制图形的参数,例如绘制线段就需要提供两个端点,使用glVertex2i(double)提供线段端点。

glBegin(GL_LINES);  //开始绘制线段
glVertex2i(180, 15); //设置线段的点
glVertex2i(10, 145);
glEnd(); //结束绘制线段

六、OpenGL的出错处理

OpenGL和GLU记录错误的方法很简单,当OpenGL发现在对基本库子程序或GLU子程序的一次调用中有错误时,就在内部记录一个出错编码,而造成出错的子程序被忽略(因此该错误不影响OpenGL的内部状态,也不影响帧缓存的内容)。但是,OpenGL每次只记录一个出错编码。一旦出现一个出错编码,在你的程序明确查询OpenGL出错状态之前不会再记录另外的出错编码:

GLenum code;
code = glGetError();

该调用返回当前的出错编码并清除内部出错标志。如果返回值等于OpenGL符号常数​​GL_NO_ERROR​​,则什么事也没有。任何其它返回值都表示出现问题,常见的OpenGL出错编码的符号常数如下表:

符号常数

含义

GL_INVALID_ENUM

GLenum的参数超出范围

GL_INVALID_VALUE

数值参数超出范围

GL_INVALID_OPERATION

当前OpenGL状态中有一个操作非法

GL_STACK_OVERFLOW

该命令将引起栈向上溢出

GL_STACK_UNDERFLOW

该命令将引起栈向下溢出

GL_OUT_OF_MEMORY

没有足够的存储空间可以用于执行命令

这些符号常数是有帮助的,但直接打印出来并不提供特别的信息。幸好GLU库包含一个函数,可以为每个GLU和GL错误返回一个描述性字符串,并将其作为一个参数传递给该函数。返回值可以使用fprintf来打印,例如:

#include <stdio.h>

GLenum code;
const GLubyte *string;

code = glGetError();
string = glErrorString(code);
fprintf(stderr, "OpenGL error: %s\n", string);

​gluErrorString​​返回的值指向位于GLU库内部的一个字符串。这不是一个动态分配的字符串,所以不能由我们的程序重新分配。同样也不能由我们的程序对其进行修改,所以有字符串声明的常数修改器。

有了出错报告函数,在写程序时使用一个未使用过的功能,或者当程序生成的图像中看到不正常或非预期的结果时,常常需要安排出错检查。

#include <stdio.h>

GLenum errorCheck()
{
GLenum code;
const GLubyte *string;
code = glGetError();
if(code != GL_NO_ERROR) {
string = gluErrorString(code);
fprintf(stderr, "OpenGL erro : %s\n", string);
}

return code;
}