在 这篇文章 中我们介绍了使用 glut 库来创建和管理窗口,这里我们再介绍另外两个可用于 OpenGL 窗口管理的库 glfw 和 SFML。
glfw
glfw 是一个专门针对 OpenGL 编写的 C 语言库,它提供了渲染物体所需的最低限度的接口。
配置环境
下载 glfw,可以下载编译好的二进制包,也可以下载源代码;这里我们下载源代码,然后手动进行编译。
源码中没有工程文件,需要手动创建一个工程,然后进行配置。这个过程比较笨重,可以使用一个工程文件生成工具 CMake,它可以使用预定义好的 CMake 脚本自动生成工程文件。
- 第一步,点击 Browse Source 按钮,选择源路径,这里选择 glfw 的根目录;
- 第二步,点击 Browse Build 按钮,选择生成的项目文件存放位置,可以在 glfw 根目录下新建一个 build 目录;
- 第三步,点击 Configure 按钮,选择要生成的项目类型,如果是 Windows 平台则选择电脑上安装的 vs 版本即可;
- 第四步,Generate 按钮,则会在 build 目录下生成一个 vs 解决方案文件;
- 第五步,打开 glfw.sln,生成项目 glfw,会生成一个 glfw3.lib,这是一个静态链接库文件;如果要生成动态链接库,把项目属性改成动态链接库就行了。
使用的方法就很简单了,和之前使用 glew 一样,三步曲操作:
- 把 glfw3.h 和 glfw3native.h 文件拷贝到 vs 目录下或项目目录下;
- 把 glfw3.lib 拷贝到 vs 目录下或项目目录下;
- 如果是使用动态链接库的形式,把 glfw3.dll 拷贝到系统目录下或项目可执行文件目录下。
之前使用 glut 的时候不需要配置环境,只需要引入头文件 glut.h
,那是因为这个头文件帮我们做了很多事情。打开 glut.h
,可以看到下面几行代码。
1 |
#pragma comment
命令用于添加要使用的静态库,其作用和在 链接器->输入 添加相应的库是一样的。在 glut.h
中添加了对 OpenGL 核心库 opengl32.lib
,实用库 glu32.lib
和工具库 glut32.lib
的引用。还 include 了 gl.h
和 glu.h
这两个头文件,所以在我们的项目中只需要 incldue glut.h
即可。
现在我们使用 glfw 库来代替 glut 库,则需要手动添加对 opengl32.lib, glu32.lib, glfw3.lib
的引用,还要在 #include <glfw3.h>
之前 include gl.h
和 glu.h
这两个头文件。如果想省略这些工作,可以模仿 glut.h
,在 glfw3.h
中添加下面的代码。
1 |
创建窗口
先上完整的代码。
1 |
|
- 注意
glew.h
在所有 OpenGL 头文件中必须第一个 include。 glfwInit
用于初始化 glfw。glfwWindowHint
用于初始化 OpenGL 参数。GLFW_CONTEXT_VERSION_MAJOR 指定 OpenGL Context 的主版本号。
GLFW_CONTEXT_VERSION_MINOR 指定 OpenGL Context 的次版本号,这两个版本号均不能高于系统上的 OpenGL 版本。
GLFW_OPENGL_PROFILE 指定使用的 OpenGL 模式为核心模式。
GLFW_RESIZEABLE 指定窗口是否可以改变大小,如果设置成 false,则窗口大小不能改且最大化按钮不能用。glfwCreateWindow
创建一个窗口,前两个参数设置窗口宽高,第三个参数设置窗口标题,最后两个参数设置为空即可。glfwMakeContextCurrent
把创建的窗口设置成 OpenGL Context。前面讲过 Context 是 OpenGL 状态的合集,没有 Context,OpenGL 将不存在;之前使用 glut 只要创建出窗口就行了,使用 glfw 要手动把窗口设置成 OpenGL 的 Context。
glViewport
来设置视口大小,这一步暂时不设置也行,其默认的视口大小就是窗口初始的大小,后面讲到摄像机的时候会再讨论视口的知识。glfwGetFramebufferSize
可以得到窗口的大小,这个函数中在窗口大小改变时很有用。- 最后我们使用一个 while 循环让程序阻塞,在 while 循环条件中调用
glfwWindowShouldClose
函数判断窗口是否关闭,如果关闭则跳出循环。glut 阻塞程序的方式是使用 glMainLoop 函数。
- 在 while 循环中先调用
glfwPollEvents
处理窗口事件,然后开始渲染,glfwSwapBuffers
和glutSwapBuffers
的作用是一样的。 - 最后窗口关闭时调用
glfwTerminate
来释放 glfw 分配的内存。
整个过程很简单,在前面的例子中我们都会使用一个 init 函数来初始化一些数据,然后使用一个 render 或 display 函数来进行渲染,在 glut 中是在 glutCreateWindow 之后调用 init 函数,然后使用 glutDisplayFunc(display) 来注册回调函数。在 glfw 中同样是在 glfwCreateWindow 之后调用 init 函数,然后在 while 循环中调用 display 函数即可。这里有一点不同的是在 glut 中,display 只有在窗口改变时才会调用,而在 glfw 中 display 函数是每一帧都会调用的。
键盘事件
glfw 中的键盘事件是通过回调函数设置的,函数原型如下。
1 | void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode); |
- 第二个参数是按下的键的 ascii 码;
- 第三个参数暂没用到;
- 第四个参数表示当前是按下动作
GL_PRESS
还是松开动作GL_RELEASE
; - 第五个参数表示当前是否按下
Ctrl, Alt, Shift
等特殊键。
注册事件方法如下。
1 | glfwSetKeyCallback(window, key_callback); |
下面是一个实例。
1 | void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode) |
在这个回调函数里,首先检测到按下 esc 键然后松开,则把窗口关闭;然后检测按下 ctr+1 或 ctr+2 键松开,修改顶点数据,在下一帧绘制的时候就会生效。
鼠标事件
glfw 中鼠标事件共四种,分别是移动鼠标事件、鼠标进入离开事件、鼠标按键事件和鼠标滚轮事件。
移动鼠标事件
1 | glfwSetCursorPosCallback(window, mouse_callback); |
x y 是当前鼠标相对于窗口的位置。
鼠标进入离开事件
1 | glfwSetCursorEnterCallback(window, mouse_enter_callback); |
enter 等于 0 表示鼠标离开,enter 等于 1 表示鼠标进入。
鼠标按键事件
1 | glfwSetMouseButtonCallback(window, mouse_down_callback); |
- 第二个参数指按下的是哪个键,取值范围有
GLFW_MOUSE_BUTTON_LEFT, GL_MOUSE_BUTTON_MIDDLE, GL_MOUSE_BUTTON_RIGHT
; - 第三个参数表示是按下动作还是松开动作;
- 最后一个参数表示当前是否有按下
ctrl, shift, alt
键。
鼠标滚轮事件
1 | glfwSetScrollCallback(window, mouse_scroll_callback); |
xoffset 和 yoffset 表示两个方向的滚动值,一般的鼠标滚动事件都只有垂直方向,即 xoffset 恒定为 0,yoffset = 1 表示向上滚动,yoffset = -1 表示向下滚动。
SFML
SFML,全称是简单快速的多媒体库,用于快速构建 PC 上的多媒体应用和游戏,其本身内嵌了 OpenGL,能够快速创建 OpenGL Context,而且无需包含其它库。SFML 比 freeglut 功能更强大,使用起来也简单,除了 OpenGL 环境,SFML 本身包括 系统、窗口、图形、网络、音频 五个模块,窗口模块用于创建 OpenGL 环境,其它模块可快速实现 OpenGL 之外的功能。
关于 SFML,有专门的 系列文章 介绍,这里就不展开讲了。