很多同学在初学计算机图形学时,都要去配置OpenGL环境,其中涉及多个库的选择和一些专有名词,让人十分困惑。本文对OpenGL相关的一些库和专有名词进行一些介绍,希望能够帮助到你。另外,如果你正在配置OpenGL而不知道选择那些库,或者配置环境总是出错(我当时也弄了好久),我推荐GLEW+GLFW+GLM(这是许多现行OpenGL工程和教程所使用的配置),并推荐你看这篇文章WIN10+VS2019+GLFW和GLEW搭建OpenGL开发环境简易版。
如果你想进一步学习OpenGL,我推荐LearnOpenGL网站和OpenGL编程指南(又称‘OpenGL红宝书’)这本书。
In a word:
OpenGL: 开放图形库,或者说是一个图形标准。
GLUT,FreeGLUT,GLFW : 完成窗口管理,事件处理等OpenGL不包含但必要的工作。
GLEW, GLAD, GL3W: 获取OpenGL拓展函数的地址,并绑定到对应的函数指针上,方便对函数的调用。
GLSL:OpenGL的着色器语言。
GLM:OpenGL数学库,封装了矩阵和变换等的一些操作。
OpenGL : Open Graphics Library,人称开放图形库,是最为广泛运用的一个图形库。但实际上,OpenGL并不是一个’库’,因为你从他的官网无法直接下载到二进制链接库或者源代码。那么OpenGL是什么呢?OpenGL是一个’标准’ 或者’规范’(Specification)。它 声明(只是声明了函数,并没有实现) 了一些C语言函数,例如glBegin(),glColor3f(),glVertex2f()(注意上面函数都有参数,这里只是没有打出来,希望不会误导你)等等,合理地调用这些函数可以绘制你从电脑屏幕上看到过的任何内容。上面提到OpenGL声明函数,而没有实现函数,那么函数的实现在哪儿呢?答案是在显卡的驱动程序里面,意味着你需要一些方法来获取这些函数,在后文会提到。
GLUT:OpenGL Utility Toolkit,人称OpenGL实用工具集。GLUT是一个’库’(意味着可以直接下载到源代码或者二进制链接库),它有什么用呢?OpenGL基本上只有绘图功能,但是你如果向画出一个红色的三角形,点击一下让它变成蓝色,这就有点超出OpenGL的能力范围了,这正是GLUT存在的意义。GLUT可以用来创建窗口glutCreateWindow,为你的程序增加交互性,例如如果你想要在鼠标按下时做点什么,你可以调用glutMouseFunc(void (*func)(int button, int state, int x, int y)),为你的程序绑定一个回调函数,这样的话,在鼠标按下时,你就可以及时地捕获并且处理了。注意:GLUT已经是上个世纪的老古董了,已经停止更新了20多年,意味着它或多或少有些跟不上时代了,但是GLUT相对简单的接口对初学者还是十分有利的。
(如果你看不懂这个函数的参数是什么,我解释一下:这个函数的参数是一个’函数指针’,这个函数指针指向的函数有(int button, int state, int x, int y)这样的四个参数。例如你想要实现上述的将红色的三角形变成蓝色,你可以这样写
然后在主函数里调用glutMouseFunc(changeColor);
注意这里的changeColor不要加括号写成changeColor(),也不要带参数,因为glutMouseFunc()的参数是一个函数指针,changeColor不带括号时是一个函数指针(且是常函数指针),带括号之后得到的是他的返回值void。当鼠标点击之后,底层会帮助你调用你的函数并为它填上参数。
)
FreeGLUT:这就不解释它的全称了,FreeGLUT是GLUT的超集,即GLUT ∈ FreeGLUT,FreeGLUT一直在持续更新。FreeGLUT所做的和GLUT差不多,只不过它更加稳定,功能更加强大。如果你愿意,可以FreeGLUT看成是GLUT的升级+拓展版。
GLFW:其实GLFW的官网也没有说明GLFW到底是什么的简写,但是人们普遍认为他的全称是Graphics Library For Windows。GLFW的本质同GLUT,它存在的目的也是实现创建,管理窗口,增加交互性(鼠标,键盘,和其他设备),和其他是操作系统打交道的事情。但是,与之不同的是,GLFW更加底层,也相对复杂,且一直在更新。
GLEW:如果你已经学习一些OpenGL编程经验,也许你会发现你的C/C++编译器标准库里面的’GL.h’头文件有很多OpenGL函数(或者说是接口)是没有的,例如glGenBuffer,glCompileShader等等。为什么会这样呢?答案是你的标准库里面的OpenGL太过陈旧了。
这是MSVC编译器下的GL.h头文件,如果没猜错的话,这是上个世纪的古董了(1996年)。那么新版的,更高效的OpenGL函数要在哪儿去找呢?答案是GLEW!!GLEW是Graphics Library Extensions Wrangler的简称,这里中文不太好翻译就不翻译了。GLEW可以把拓展的函数或者是新版OpenGL的函数带到你的程序中,当然,它也包含了旧版OpenGL的接口和定义等等。GLEW是如何把这些额外的内容带到你的程序中去的呢?以glCompileShader函数为例:
这是glew.h中glCompileShader的声明,可以看出glCompileShader是一个宏。根据宏替换的规则,glCompileShader = GLEW_GET_FUN(__glewCompileShader)。那GLEW_GET_FUN(__glewCompileShader)又是啥呢?去找GLEW_GET_FUN()的定义,可以发现GLEW_GET_FUN也是一个宏
这个宏定义了GLEW_GET_FUN(x) = x,根据前面的,可以推知glCompileShader = __glewCompileShader。接着去找__glewCompileShader的定义,可以找到这里
去找GLEW_FUN_EXPORT的定义,可以找到这里
可以发现GLEW_FUN_EXPORT = GLEWAPI,接着去找GLEWAPI的定义,可以找到这里
extern表明这里是一个纯声明的外部变量,__declspec是Microsoft c++独有的关键字,可以对标准C++进行扩充,__declspec(dllimport)表明了这个函数是在别的dll(动态链接库)中,我要使用它,需要动态链接到我的程序中。
PFNGLCOMPILESHADERPROC是一个typedef的函数指针类型
GLAPIENTRY 也是一个宏,展开为__stdcall,表示一个标准调用的函数(如果你不明白__stdcall的含义,可以搜索一下: __stdcall,__thiscall,__cdecl的区别 )
至此,我们可以知道*glCompileShader 实际上被声明为 extern __declspec(dllimport) void (__stdcall glCompileShader)(GLuint shader),代表了一个外部的一个函数指针 。没错,你对新版OpenGL接口的访问都是通过一个个函数指针来实现的,那么程序怎么知道这些函数指针该指向哪里呢?你是否还记得你在你的程序里面调用了glewInit()这个函数,这个函数会加载库并找到其中的函数,然后为这些函数指针赋值,然后你就可以安心滴调用新版本的OpenGL函数了。当然,如果你在调用glewInit之前调用了glCompileShader等函数,由于这些函数都还是“野指针”的状态,你的程序会获得一个SegmentFault。
GLAD :GL ADdress,从名字看大致可以得知这也是一个获取函数地址的库,实际上,GLAD在功能上和GLEW等价。完成找到地址这一功能的库还有GL3W等。
GLSL :GL Shading Language,开放图形库着色语言,这是一个C风格编译型的着色器语言,可以用来为OpenGL程序编写着色器,GLSL编写的着色器大致长这个样子:
/*顶点着色器*/
#version 460 core
layout(location = 0) in vec3 pos;
uniform mat4 transform;
void main(){
gl_Position = transform*vec4(pos,1.0);
}
/*片元着色器*/
#version 460 core
uniform vec3 color;
out vec4 co;
void main(){
co = vec4(color,1.0) ;
}
着色器大致可以定义为在GPU上运行的程序,借助GPU动辄数千的核心数,着色器程序可以实现真正的并行流水线运行,大大提高渲染速率。如果想进一步了解一下如何编写GLSL着色器,可以LearnOpenGL上看这篇文章,链接:LearnOpenGL。
GLM: GL Math library,是OpenGL中常用的数学库。它可以用来完成矩阵,向量运算和生成一些变换矩阵,如模型矩阵,观察矩阵,投影矩阵(MVP矩阵)等等。使用GLM可以大大地简化一些数学上的操作。
好了,大致就写到这里了。本人也是一个初学者,很多地方还没有搞明白。如果有错误的话,欢迎指正。