前言

本文在 Manjaro Linux 下进行操作,学习如何在 Linux 下结合 CMake 搭建 OpenGL 环境。

编译GLFW

我们在绘制图形的时候,需要创建一个 OpenGL 上下文和一个窗口,处理用户的输入,这些操作在每个操作系统上都不一样, OpenGL 将这些操作抽象出去,所以这些工作就需要程序员自己来做了。不过好消息是已经有第三方库做了这件事情了,比如 GLUTSDLSFMLGLFW 。这里我们选用 GLFW

下面的步骤教大家编译一个 GLFW 的静态库。

1
2
3
4
5
git clone https://github.com/glfw/glfw.git
cd glfw
mkdir build && cd build
cmake ..
make

执行完以上步骤会在 build/src 目录下看到 libglfw3.a 这个静态库,这个文件后面会要用到。

GLAD

前面我们说道 OpenGL 是一个规范,它由驱动厂商针对特定的显卡实现的, OpenGL 驱动的版本众多,它的函数位置无法在编译时确定下来,需要在运行时查询。这件事同样落在了开发者身上,同样幸运的是已经第三库做了就是GLAD。打开这个网址选择如下所示 点击 generate 后,会把 glad.zip 下载到本地,解压得到 srcinclude 两个目录,如下所示

1
2
3
4
5
6
7
8
.
├── include
│   ├── glad
│   │   └── glad.h
│   └── KHR
│       └── khrplatform.h
└── src
    └── glad.c

这里只有头文件,我们不需要进行编译,后面直接引入项目中使用即可。

创建一个项目

资源都准备好了,就可以开始创建项目了,执行如下命令

1
2
3
mkdir window && cd window
mkdir src include lib
touch CMakeLists.txt src/main.cpp

我们把 glfwinclude 文件夹下的 GLFW 拷贝到刚刚创建的 include 目录下,并且把 GLADincludegladKHR 两个目录也拷贝到刚刚创建的 include 目录下。

接着把 glfw/build/src 中的 libglfw3.a 拷贝到刚刚创建的 lib 目录下。

最后把 glad/src/glad.c 拷贝到刚刚创建的 src 目录下。

目录拷贝完了之后,我们还要在根目录下创建 CMakeLists.txt 文件,并且在 src 下新建 main.cpp 作为程序的入口文件。

整个目录如下所示

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
.
├── CMakeLists.txt
├── include
│   ├── glad
│   │   └── glad.h
│   ├── GLFW
│   │   ├── glfw3.h
│   │   └── glfw3native.h
│   └── KHR
│       └── khrplatform.h
├── lib
│   └── libglfw3.a
└── src
    ├── glad.c
    └── main.cpp

编写 CMake

文件都准备好了,我们来编写 CMakeLists.txt ,导入 GLFW 静态库和 GLAD 的头文件,和添加依赖。如下所示

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
cmake_minimum_required(VERSION 3.12)

project(window)

set(CMAKE_CXX_STANDARD 14)
# 添加头文件所在的目录
include_directories(${PROJECT_SOURCE_DIR}/include)

# 导入 glfw 静态库
add_library(glfw STATIC IMPORTED)
set_target_properties(glfw PROPERTIES IMPORTED_LOCATION ${PROJECT_SOURCE_DIR}/lib/libglfw3.a)

# 编译成可执行文件
add_executable(${PROJECT_NAME} src/main.cpp src/glad.c)
# 需要链接的库
target_link_libraries(${PROJECT_NAME} glfw GL m Xrandr Xi X11 pthread dl Xcursor)

显示窗口

CMakeLists.txt 编写好之后,我们就来编写程序实现显示一个窗口

初始化GLFW

在显示窗口之前,还需要先初始化 GLFW 这些都是固定的代码,如下所示

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#include <iostream>
#include <glad/glad.h>
#include <GLFW/glfw3.h>

int main(int argc, char *argv[])
{
    // 初始化 glfw,并设置 版本为3.3,使用核心模式
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    return 0;
}

初始好了 GLFW 之后就可以创建窗口了

1
2
3
4
5
6
7
GLFWwindow* window = glfwCreateWindow(800, 600, "Window", NULL, NULL);
if (window == NULL) {
    std::cout << "Failed to create GLFW window" << std::endl;
    glfwTerminate();
    return -1;
}
glfwMakeContextCurrent(window);

我们使用 glfwCreateWindow 来创建一个窗口,并且指定窗口的大小为 800*600,窗口的标题为 Window

创建完窗口我把窗口的上下文设置为当前的上下文(Context)

这时候如果运行会看到不窗口,因为执行完之后,程序马上就退出了。

初始化 GLAD

在使用 OpenGL 函数之前我们需要使用 GLAD 来帮我们管理 OpenGL 的函数指针,如下所示

1
2
3
4
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
    std::cout << "Failed to initialize GLAD" << std::endl;
    return -1;
}

展示窗口

为了不让程序马上退出,我们需要在一个循环中来处理程序是否需要退出

1
2
3
4
while(!glfwWindowShouldClose(window)) {
    // 获取键盘和鼠标事件,这里用于检测是否点击了关闭按钮
    glfwPollEvents();
}

接着运行程序,窗口就显示出来了,但是你会发现窗口是透明的,这是因为我们没有在上面绘制任何东西,点击关闭可以关闭。

glfwWindowShouldClose 是用来检测窗口是否需要关闭,如果你点击了关闭这个函数就会返回 true

绘制背景颜色

1
2
3
4
5
6
while(!glfwWindowShouldClose(window)) {
    glClearColor(0.f, 0.f, 1.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    glfwSwapBuffers(window);
    glfwPollEvents();
}

在循环中,我们使用 glClearColor 来清空屏幕的颜色,也就相当与给屏幕设置颜色。 glClearColor 使用的是 rgba 表示法。所以上面程序运行会看到一个蓝色的窗口。

glClear 用来清除颜色缓冲,参数可以是 GL_COLOR_BUFFER_BITGL_DEPTH_BUFFER_BITGL_STENCIL_BUFFER_BIT 在这个程序中只使用到了颜色,所以用 GL_COLOR_BUFFER_BIT 就可以了。

调用 glClear 后,就会使用 glClearColor 中设置的颜色了。

接下来是调用 glfwSwapBuffers 函数交换颜色缓冲,在 OpenGL 中使用的是双缓冲(Double Buffer),这么做是由于单缓冲会导致图像闪烁问题。图像绘制需要一定的时间,所以所有的渲染都是在后缓冲上进行,渲染完成后交换两个缓冲,这样图像显示出来就会比较自然。不然看到的可能是前一帧的部分和后一帧的部分。

总结

今天第一次学 OpenGL 就先到这,我们来总结一下套路

  1. 首先要先编译好 GLFW ,用来创建窗口,处理上下文,接收一些输入事件(鼠标,键盘等等)
  2. 下载 GLAD 用来管理 OpenGL 的函数指针,便于我们使用 OpenGL 提供的 API
  3. 在代码中初始化 GLFWGLAD
  4. 使用 GLFW 创建窗口,绑定上下文
  5. 使用 OpenGL 提供的函数进行绘制,处理用户的输入

完整代码

 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
#include <iostream>
#include <glad/glad.h>
#include <GLFW/glfw3.h>

int main(int argc, char *argv[]){

    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    GLFWwindow* window = glfwCreateWindow(800, 600, "Window", NULL, NULL);
    if (window == NULL) {
        std::cout << "Failed to create GLFW window" << std::endl;
        glfwTerminate();
    }
    glfwMakeContextCurrent(window);

    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
        std::cout << "Failed to initialize GLAD" << std::endl;
        return -1;
    }

    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);

    while(!glfwWindowShouldClose(window)) {
        glClearColor(0.f, 0.f, 1.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);
        glfwSwapBuffers(window);
        glfwPollEvents();
    }

    glfwTerminate();
    return 0;
}

参考

你好,窗口 - LearnOpenGL CN