1.编译GLFW生成LIB库文件

由于在Windows平台下初始化OpenGL非常麻烦,不仅需要创建OpenGL上下文,而且更麻烦的是调用OpenGL的GPU驱动的函数(具体多麻烦查看我的博文)。所以推荐使用第三方已经写好的库GLFW,类似的库还有GLUT。 GLFW是跨平台的抽象层,所以同一份代码在Windows和Linux下都可以运行。

GLFW提供了创建窗口,创建OpenGL上下文,管理键盘和鼠标的事件,甚至还提供多线程管理等跨平台的功能。功能越来越多了。

在GLFW官网上,找到他们的源码页,并下载到本地。

进入到glfw目录下

创建build文件夹,然后使用cmake构建出glfw项目文件。

1
2
3
4
cd glfw
mkdir build
cd build
cmake

用VS打开GLFW.sln

执行项目,执行完毕后,观察src/Release文件夹下多了我们需要的静态链接库文件glfw3.lib

2.使用GLFW库

1.以动态库方式使用

以后使用GLFW库时,在项目中添加包含的头目录和库目录

打开VS开发工具选择下面菜单: 头目录:

“Project>Properties>Configuration Properties>VC++ Directories>General> Include Directories”

添加

“D:\libs\glfw\include”

(上面路劲你根据自己情况填写)

2.以静态库方式使用

在这里我建议直接把静态链接库文件拷贝到项目下。

然后在项目代码中将库文件包含进去。

1
#pragma comment (lib, “glfw3.lib”)

这样生成的代码就包含静态库的内容了,移植时减少了bug的风险。

代码样例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include <GLFW/glfw3.h>
#include <gl/GL.h>
#include <iostream>

#pragma comment (lib, "opengl32.lib")
#pragma comment (lib, "glfw3.lib")

void error_callback(int code, const char* description);

int main(void)
{
    GLFWwindow* window;

    /* Handle GLFW Error */
    glfwSetErrorCallback(error_callback);

    /* Initialize the library */
    if (!glfwInit())
        return -1;

    /* Create a windowed mode window and its OpenGL context */
    window = glfwCreateWindow(640, 480, "Hello World", NULL, NULL);
    if (!window)
    {
        glfwTerminate();
        return -1;
    }

    /* Make the window's context current */
    glfwMakeContextCurrent(window);

    /* Loop until the user closes the window */
    while (!glfwWindowShouldClose(window))
    {
        /* Render here */
        glClear(GL_COLOR_BUFFER_BIT);

        /* Swap front and back buffers */
        glfwSwapBuffers(window);

        /* Poll for and process events */
        glfwPollEvents();
    }

    glfwTerminate();
    return 0;
}

void error_callback(int code, const char* description)
{
    std::cout << code << ":" << description << std::endl;
}

3. GLFW的基本用法

GLFW初始化和终止

在使用GLFW大部分函数之前需要对GLFW库进行初始化,初始化的工作内容包括检查平台包含的特定功能。GLFW库初始化通过glfwInit进行。

1
2
3
4
if(!glfwInit())
{
	// handle initialization failure
}

如果初始化成功则会返回GLFW_TRUE、否则返回GLFW_FALSE。 在初始化之前,GLFW函数中只有下面几个函数可以使用,调用其他函数将会报GLFW_NOT_INITALIZED错误。

1
2
3
4
5
glfwGetVersion
glfwGetVersionString
glfwGetError
glfwSetErrorCallback
glfwInitHint

在应用程序退出之前应先释放GLFW库占用的系统资源。这可以通过glfwTerminate实现。2.2 GLFW错误处理 有些GLFW函数出错会返回对你解决错误没什么用的错误值,更有甚者有些函数还没返回值。所以处理GLFW的错误就需要独立的通道。在GLFW出现错误时会抛出错误代码,我们可以通过glfwGetError捕获。如下所示:

1
2
3
4
5
6
7
8
const char* description;
int code = glfwGetError(&description);
if(code!=GLFW_NO_ERROR)
{
	// display error message...

	// handle error ...
}

字符指针description会指向以UTF-8码形式存储的错误信息内存处。 然而更推荐的做法是设置错误回调函数glfwSetErrorCallback。这样每次glfw出现错误都会调用回调函数。

1
2
3
4
5
6
glfwSetErrorCallback(error_callback);

void error_callback(int code, const char* description)
{
    // display error...
}

创建窗口和OpenGL上下文

GLFW库通过glfwCreateWindow函数进行创建OpenGL上下文和创建窗口。创建之前若想定制自已想要的窗口和上下文而不是系统默认的,可通过window hint设置属性。你可以创建多个窗口和上下文,但是,仅被make current的窗口和上下文是起作用的。要想查询已经创建好的窗口和上下文属性,可通过glfwGetWindowAttrib、glfwGetWindowSize、glfwGetFramebufferSize查询。

1
2
3
4
5
6
7
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
GLFWwindow* window = glfwCreateWindow(640, 480, "My Title", NULL, NULL);
if (!window)
{
    // Window or context creation failed
}

上面代码设置了OpenGL上下文的最低版本,如果机器不满足最低版本的限制,将会创建失败。 如果不在需要一个窗口了,摧毁它。

1
glfwDestroyWindow(window);

Make Context Current

使用OpenGL的API必须有而且只能有一个OpenGL上下文被设置成Current。 OpenGL上下文就是一个个状态机,显卡驱动程序只对Current上下文起作用。

GLFW设置current的语句和Windows的很像。

1
glfwMakeContextCurrent(window);

注:如果你是通过glad或gl3w来使用OpenGL,你就必需在使用它们之前拥有一个current context。

检查窗口关闭的标识

在GLFW中,每个窗口都有一个标识,用来标记窗口是否应该被关闭。当用户关闭了一个窗口,该标记就被设置成了1,所以窗口并没有被关闭,你需要时刻检查这个标记。 检查的方式是通过函数glfwWindowShouldClose。 返回的就是窗口关闭的标识值。

1
2
3
4
while (!glfwWindowShouldClose(window))
{
    // Keep running
}

你也可以设置窗口的回调函数,每次关闭窗口时都会触发该函数。

如下所示:

1
2
3
4
5
6
glfwSetWindowCloseCallback(window, window_close_callback);
...
void window_close_callback(GLFWwindow* window)
{
    glfwSetWindowShouldClose(window, 0);
}

键盘事件响应

GLFW提供了设置键盘响应的回调函数glfwSetKeyCallback,如下所示:

1
2
3
4
5
6
7
glfwSetKeyCallback(window, key_callback);

void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
    if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
        glfwSetWindowShouldClose(window, GLFW_TRUE);
}

交换缓冲区与处理事件

GLFW创建的窗口有两个缓冲区,前缓冲区和后缓冲区,渲染是在后缓冲区上进行的,一旦缓冲区满了,就需要交换前后缓冲区,通过glfwSwapBuffers(window)。 事件发生时,并不是立即得到处理的,而是暂时挂起。这些挂起的事件需要定期处理,通常在缓冲区交换的每一帧后完成。

如下所示:

1
2
3
4
/* Swap front and back buffers */
glfwSwapBuffers(window);
/* Poll for and process events */
glfwPollEvents();

参考资料

https://www.glfw.org/docs/latest/quick.html#quick_steps