第一课
1.1 简单OpenGL程序包含的内容:
首先,需要包含头文件#include <GL/glut.h>,这是GLUT的头文件。 本来OpenGL程序一般还要包含<GL/gl.h>和<GL/glu.h>,但GLUT的头文件中已经自动将这两个文件包含了,不必再次包含。
以glut开头的函数都是GLUT工具包所提供的函数,下面对用到的几个函数进行介绍:
1、glutInit,对GLUT进行初始化,这个函数必须在其它的GLUT使用之前调用一次。其格式比较死板,一般照抄这句glutInit(&argc, argv)就可以了。
2、glutInitDisplayMode,设置显示方式,其中GLUT_RGB表示使用RGB颜色,与之对应的还有GLUT_INDEX(表示使用索引颜色)。GLUT_SINGLE表示使用单缓冲,与之对应的还有GLUT_DOUBLE(使用双缓冲)。
3、glutInitWindowPosition,这个简单,设置窗口在屏幕中的位置。
4、glutInitWindowSize,这个也简单,设置窗口的大小。
5、glutCreateWindow,根据前面设置的信息创建窗口。参数将被作为窗口的标题。注意:窗口被创建后,并不立即显示到屏幕上。需要调用glutMainLoop才能看到窗口。
6、glutDisplayFunc,设置一个函数,当需要进行画图时,这个函数就会被调用。(这个说法不够准确,但准确的说法可能初学者不太好理解,暂时这样说吧)。
7、glutMainLoop,进行一个消息循环。(这个可能初学者也不太明白,现在只需要知道这个函数可以显示窗口,并且等待窗口关闭后才会返回,这就足够了。)
种以gl开头的函数都是OpenGL的标准函数,下面对用到的函数进行介绍。
1、glClear,清除。GL_COLOR_BUFFER_BIT表示清除颜色,glClear函数还可以清除其它的东西,但这里不作介绍。
2、glRectf,画一个矩形。四个参数分别表示了位于对角线上的两个点的横、纵坐标。
3、glFlush,保证前面的OpenGL命令立即执行(而不是让它们在缓冲区中等待)。其作用跟fflush(stdout)类似。
第二课
2.1 绘制简单几何图形
点:OpenGL中的点将被画成单个的像素
直线:OpenGL的“直线”概念与数学上的“线段”接近,它可以由两个端点来确定。
多边形:一个多边形必须是一个“凸多边形”(其定义为:多边形内任意两点所确定的线段都在多边形内,由此也可以推导出,凸多边形不能是空心的),通常用三角形
通过点、直线和多边形,就可以组合成各种几何图形
2.2 指定顶点
OpenGL提供了一系列函数。它们都以glVertex开头,后面跟一个数字和1~2个字母。例如:
glVertex2d glVertex2f glVertex3f glVertex3fv 等等。
数字表示参数的个数,2表示有两个参数,3表示三个,4表示四个。
字母表示参数的类型,s表示16位整数(OpenGL中将这个类型定义为GLshort), i表示32位整数(OpenGL中将这个类型定义为GLint和GLsizei), f表示32位浮点数(OpenGL中将这个类型定义为GLfloat和GLclampf), d表示64位浮点数(OpenGL中将这个类型定义为GLdouble和GLclampd)。 v表示传递的几个参数将使用指针的方式
2.3 开始绘图
OpenGL要求:指定顶点的命令必须包含在glBegin函数之后,glEnd函数之前(否则指定的顶点将被忽略)。并由glBegin来指明如何使用这些点。 例如:
glBegin(GL_POINTS);
glVertex2f(0.0f, 0.0f);
glVertex2f(0.5f, 0.0f);
glEnd();
则这两个点将分别被画出来。如果将GL_POINTS替换成GL_LINES,则两个点将被认为是直线的两个端点,OpenGL将会画出一条直线。 还可以指定更多的顶点,然后画出更复杂的图形。
GL_POINTS和GL_LINES,还有GL_LINE_STRIP,GL_LINE_LOOP,GL_TRIANGLES,GL_TRIANGLE_STRIP,GL_TRIANGLE_FAN等,每种方式的大致效果见下图:
(见图1)
第三课
3.1 关于点
点大小:默认值为1.0f,函数原型为void glPointSize(GLfloat size);
例子:
void myDisplay(void)
{
glClear(GL_COLOR_BUFFER_BIT);
glPointSize(5.0f);
glBegin(GL_POINTS);
……
glEnd();
glFlush();
}
3.2 关于直线
3.2.1 直线线宽
函数原型void glLineWidth(GLfloat width);
其用法跟glPointSize类似。
3.2.2 虚线
首先,使用glEnable(GL_LINE_STIPPLE);来启动虚线模式(使用glDisable(GL_LINE_STIPPLE)可以关闭之)。 然后,使用glLineStipple来设置虚线的样式。 void glLineStipple(GLint factor, GLushort pattern); pattern是由1和0组成的长度为16的序列,从最低位开始看,如果为1,则直线上接下来应该画的factor个点将被画为实的;如果为0,则直线上接下来应该画的factor个点将被画为虚的。
3.3 关于多边形
3.3.1 多边形的两面及其绘制方式
虽然我们目前还没有真正的使用三维坐标来画图,但是建立一些三维的概念还是必要的。 从三维的角度来看,一个多边形具有两个面。每一个面都可以设置不同的绘制方式:填充、只绘制边缘轮廓线、只绘制顶点,其中“填充”是默认的方式。可以为两个面分别设置不同的方式。
glPolygonMode(GL_FRONT, GL_FILL); // 设置正面为填充方式 glPolygonMode(GL_BACK, GL_LINE); // 设置反面为边缘绘制方式 glPolygonMode(GL_FRONT_AND_BACK, GL_POINT); // 设置两面均为顶点绘制方式
3.3.2 反转
一般约定为“顶点以逆时针顺序出现在屏幕上的面”为“正面”,另一个面即成为“反面”。生活中常见的物体表面,通常都可以用这样的“正面”和“反面”,“合理的”被表现出来。但也有一些表面比较特殊。例如“麦比乌斯带”,可以全部使用“正面”或全部使用“背面”来表示。 可以通过glFrontFace函数来交换“正面”和“反面”的概念。
glFrontFace(GL_CCW); // 设置CCW方向为“正面”,CCW即CounterClockWise,逆时针 glFrontFace(GL_CW); // 设置CW方向为“正面”,CW即ClockWise,顺时针
*有例程OpenGL4
3.3.3 剔除多边形表面
在三维空间中,一个多边形虽然有两个面,但我们无法看见背面的那些多边形,而一些多边形虽然是正面的,但被其他多边形所遮挡。如果将无法看见的多边形和可见的多边形同等对待,无疑会降低我们处理图形的效率。在这种时候,可以将不必要的面剔除。 首先,使用glEnable(GL_CULL_FACE);来启动剔除功能(使用glDisable(GL_CULL_FACE)可以关闭之) 然后,使用glCullFace来进行剔除。 glCullFace的参数可以是GL_FRONT,GL_BACK或者GL_FRONT_AND_BACK,分别表示剔除正面、剔除反面、剔除正反两面的多边形。 注意:剔除功能只影响多边形,而对点和直线无影响。例如,使用glCullFace(GL_FRONT_AND_BACK)后,所有的多边形都将被剔除,所以看见的就只有点和直线。
3.3.4 镂空多边形
直线可以被画成虚线,而多边形则可以进行镂空。 首先,使用glEnable(GL_POLYGON_STIPPLE);来启动镂空模式(使用glDisable(GL_POLYGON_STIPPLE)可以关闭之)。 然后,使用glPolygonStipple来设置镂空的样式。 void glPolygonStipple(const GLubyte *mask); 其中的参数mask指向一个长度为128字节的空间,它表示了一个32*32的矩形应该如何镂空。其中:第一个字节表示了最左下方的从左到右(也可以是从右到左,这个可以修改)8个像素是否镂空(1表示不镂空,显示该像素;0表示镂空,显示其后面的颜色),最后一个字节表示了最右上方的8个像素是否镂空。
如果将这样的数据保存成图片,并用专门的工具进行编辑,显然会方便很多。下面介绍如何做到这一点。
首先,用Windows自带的画笔程序新建一副图片,取名为mask.bmp,注意保存时,应该选择“单色位图”。在“图象”->“属性”对话框中,设置图片的高度和宽度均为32。用放大镜观察图片,并编辑之。黑色对应二进制零(镂空),白色对应二进制一(不镂空),编辑完毕后保存。 然后,就可以使用以下代码来获得这个Mask数组了。
static GLubyte Mask[128];
FILE *fp; fp = fopen("mask.bmp", "rb");
if( !fp ) exit(0);
// 移动文件指针到这个位置,使得再读sizeof(Mask)个字节就会遇到文件结束
// 注意-(int)sizeof(Mask)虽然不是什么好的写法,但这里它确实是正确有效的
// 如果直接写-sizeof(Mask)的话,因为sizeof取得的是一个无符号数,取负号会有问题 if( fseek(fp, -(int)sizeof(Mask), SEEK_END) ) exit(0);
// 读取sizeof(Mask)个字节到Mask
if( !fread(Mask, sizeof(Mask), 1, fp) )
exit(0);
fclose(fp);
编辑一个图片作为mask,并用上述方法取得Mask数组,运行后观察效果。 说明:绘制虚线时可以设置factor因子,但多边形的镂空无法设置factor因子。请用鼠标改变窗口的大小,观察镂空效果的变化情况。
*有例程OpenGL5