OpenGL是一个图形库,主要用于3D作图。然而,其并不提供窗口处理函数,如果想要与操作系统的窗口进行通信,需要引入GLUT也就是实用工具库。同时,由于OpenGL具有很好的平台兼容性,因此对一些函数的实现需要以指针形式进行调用。类似于动态链接库中函数的动态加载。为了便于这一部分函数的调用,进一步引入了GLEW库对函数指针的调用进行封装。编写OpenGL程序需要引入上诉函数库中的一个或者多个。下面就以一个Demo程序来介绍OpenGL程序编写的一些基本步骤。
由于OpenGL程序涉及到计算机中的两中类型的处理单元——CPU和GPU。OpenGL程序由CPU调度运行,同时部分指令在CPU对存储在系统内存中的数据进行加工进而推进程序的运行,而另外一部分指令则通过给GPU发指令进而进行图像处理,GPU处理的数据大多数情况下位于其自身所管理的专用内存中。为了协调这两部分内容进行处理,首先需要解决的问题是怎样将存储在系统内存中的数据传递到GPU专用的内存中;其次需要解决的问题是CPU应该怎样启动GPU进行运算。
为了进行上诉处理,OpenGL将CPU与GPU之间数据的传递分为三个步骤。第一步,首先利用内置的OpenGL函数生成一个ID号码,这个号码类似于Windows系统下的文件标识符概念,主要用于索引一个OpenGL内部管理的内存单元,至于这部分内存单元属于什么类型需要到第二步才知道;这一步主要是通过函数glGenBuffer实现的。该函数返回一个glInt。第二步,则是根据需要对该ID号码进行内存类型的绑定,这一步操作能够覆盖ID号码原有的内存类型,如果ID号码原来没有内存类型则对其进行初始化,这一步主要通过glBindBuffer函数完成。在经过上面两个步骤之后,GPU中用于接收系统内存中数据的“标识符”就准备好了,第三部对这部分内存进行初始化,初始化的内容来自于系统内存中,这一部分功能利用glBufferData函数完成。整个过程合并起来,代码片段如下所示。整个代码片段所完成的工作就是,将系统内存color_data中的数据给提交到color_buffer标识符所指的GL_ARRAY_BUFFER中的,至于color_buffer前面的前缀names暂时可以不看。
glGenBuffers(1, &names.color_buffer); glBindBuffer(GL_ARRAY_BUFFER, names.color_buffer); glBufferData(GL_ARRAY_BUFFER, sizeof(color_data), color_data, GL_STATIC_DRAW);数据提交到GPU专用的内存中之后,需要根据应用场景对这些数据进行适当的分配。比如,有的数据当做顶点,有的是作为颜色,有的用于控制光照等等。为了实现上诉内存类型的分配,利用glVertexAttribPointer函数进行这一部分的功能实现,该函数可以获取预先分配好的顶点着色器语言中分配的属性,在得到该属性之后,就可以对这一属性进行修改,对其修改的值是最近一个glBindBuffer操作之后绑定到OpenGL中的缓存。在更新数据之后使能该属性,以便修改生效,使能操作由glWnableVertexAttribArray函数实现。将这两部分代码结合起来看,就是利用color_data中的数据设置了相应的着色器
glVertexAttribPointer(names.attributes.inColor, 4, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*4, (void*)0); glEnableVertexAttribArray(names.attributes.inColor);经过上面的讨论,又引入了一个新的概念——着色器。那么什么是着色器呢?通俗的理解就是颜色的选择,比如在画画的时候,首先要选择画笔的颜色,因此需要一个顶点着色器,OpenGL会根据需要在给线条着色时相应的对颜色进行调整,比如颜色渐变等。在OpenGL中另一个常用的着色器是片元着色器。片元着色器选择的是线条框中的并且可见的区域的颜色,这里加入了一个定语可见,是为了区分图元和片元。图元包括点、线以及三角形,但当图元中的一部分被遮挡了,那么这部分是不需要着色的。因此为了更好的对这些区域的进行着色,引入了片元的概念。在最新版的OpenGL中为了更灵活的对着色器进行设置,引入了着色语言。
渲染语言以字符串的形式存在于程序中,或者从文本中导入。着色语言的使用包括两个部分,第一部分需要创建对应的着色器。在这一部分内容中会对着色语言进行分析,同时根据着色语言生成对应的符号,这一部分类似于编译链接过程中的编译过程,主要进行符号解析,但并不确定每一个符号的具体的地址。
shader = glCreateShader(type); glShaderSource(shader, 1, (const GLchar**)&source, &length); glCompileShader(shader);为了确定每一个符号的具体地址,需要在编译之后进行链接,在这一步中主要将相同的全局符号进行归并,同时对每一个符号的最终地址进行确定。
glAttachShader(names.program, names.fragment_shader); glLinkProgram(names.program); glUseProgram(names.program);经过上面的步骤之后,着色器程序就可以使用了。在显示的时候就会调用改程序进行对应的着色处理。着色语言的语法和C语言很类似,下面是一个示例。
#version 130 varying vec4 outColor; void main() { gl_FragColor = outColor; }
程序包括三部分,第一部分是预定义,表明需要着色器语言语法的版本要求,这里的版本号和OpenGL版本存在对应关系但不相等。第二部分,定义变量以及相应的一些属性,在这定义了一个四维向量作为颜色值,该颜色值是一个变量类型。第四部分是着色器程序的主体,主要是通过变量赋值来设置全局的属性。在着色器语法中gl_开头的变量以及函数都被保留,因为有很大一部分作为预定义的全局属性。这里的gl_FragColor就用于设置片元的颜色。
完整的可运行的代码在我的下载页中可以找到.