【OpenGL with C++】1.使用CMake+GLFW+GLAD在Window搭建项目
首先我这里需要新开一个坑,就是重新学习OpenGL。其实之前一直都有学习并且也会跟着LearnOpenGL-CN实操。可是我发现那些基本概念没有理解,导致我根本没有什么长进啊。碰巧我在Youtube看到了一个视频教程,于是我决定跟着视频教程走。
由于我也不怎么会写教程,所以我觉得应该跟着Youtube视频走。我发现CSDN似乎会把一些文章变成VIP免费观看,我真是无语了啊。我写这些文章只是因为博客每次迁移数据麻烦,还不如使用CSDN当一个免费obsidian罢了。
需要自行配置CMake环境变量,或者依赖于Clion等ide自带的环境。
创建CMake项目
首选,我们开始第一课。
创建一个OpenGL-for-Beginners文件夹,以后我们的项目都会存放在该目录。
然后再创建一个叫week_01_hello_window的文件夹用来存放,我们今天搭建的简单示例。
现在创建一个CMakeLists.txt文件,和dependencies文件夹。
接下来就是前往Youtube视频简介下的开源项目OpenGL-for-Beginners
解压该文件夹后
选择dependencies文件夹下的文件,全部拷贝至OpenGL-for-Beginners/dependencies中去。
编译GLFW依赖库
你会发现拷贝之后并没有像我下面的文件结构一样,没有dll和lib。因为这是需要你直接前往GLFW官网,下载源码后使用CMake(GUI)生成Visual
Studio项目后编译生成的。
需要使用CMake勾选共享库,并且去除这些文档和测试用例的编译。最后按照顺序依次点击即可在VS中打开该项目。最后只需要将编译编译后的文件拷贝进OpenGL-for-Beginners/dependencies/lib即可!
现在的dependencies目录的项目结构应该,像我下面的文件树一样吧。
├─dependencies
│ ├─glad
│ │ glad.h
│ │
│ ├─GLFW
│ │ glfw3.h
│ │ glfw3native.h
│ │
│ ├─glm
│ │ │ CMakeLists.txt
│ │
│ ├─KHR
│ │ khrplatform.h
│ │
│ └─lib
│ glfw3.dll
│ glfw3.pdb
│ glfw3dll.exp
│ glfw3dll.lib
│
└─week_01_hello_window│ CMakeLists.txt│└─srcConfig.hglad.cmain.cpp
编写OpenGL-for-Beginners的CMakeLists.txt文件
处理好了依赖库的的问题吧,现在搞week_01_hello_window,首先在我们OpenGL-for-Beginners/CMakeLists.txt文件,添加下面代码
cmake_minimum_required(VERSION 3.20)
project(OpenGL-for-Beginners CXX)set(GLFW_LIB_DIR ${CMAKE_CURRENT_SOURCE_DIR}/dependencies/lib)
set(DEPENDENCIES_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/dependencies)add_subdirectory(week_01_hello_window)
我们来逐行解析这个 CMake 脚本:
-
cmake_minimum_required(VERSION 3.20)
- 作用: 设置构建本项目所需的 CMake 最低版本号。
- 简洁明白: 告诉 CMake:“你需要至少是 3.20 版本才能理解并正确处理这个项目”。如果你的 CMake 版本低于 3.20,它会报错并停止。
-
project(OpenGL-for-Beginners CXX)
- 作用: 定义项目的名称,并声明项目主要使用的编程语言。
- 简洁明白: 给项目起个名字叫 “OpenGL-for-Beginners”,并告诉 CMake 这个项目是用 C++ (
CXX
) 写的。CMake 会根据这个信息来配置 C++ 编译器等设置。
-
set(GLFW_LIB_DIR ${CMAKE_CURRENT_SOURCE_DIR}/dependencies/lib)
- 作用: 创建一个 CMake 变量
GLFW_LIB_DIR
,并给它赋值。 - 简洁明白: 定义一个变量
GLFW_LIB_DIR
,它的值是当前CMakeLists.txt
文件所在目录下的dependencies/lib
这个子目录的完整路径。这个变量后面会用来指定 GLFW 库文件 (.lib, .dll) 的位置。
- 作用: 创建一个 CMake 变量
-
set(DEPENDENCIES_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/dependencies)
- 作用: 创建另一个 CMake 变量
DEPENDENCIES_INCLUDE_DIR
并赋值。 - 简洁明白: 定义一个变量
DEPENDENCIES_INCLUDE_DIR
,它的值是当前CMakeLists.txt
文件所在目录下的dependencies
这个子目录的完整路径。这个变量后面会用来指定查找头文件(比如glfw3.h
)的根目录。
- 作用: 创建另一个 CMake 变量
-
add_subdirectory(week_01_hello_window)
- 作用: 指示 CMake 去处理指定子目录下的
CMakeLists.txt
文件。 - 简洁明白: 告诉 CMake:“现在,请进入
week_01_hello_window
这个文件夹,读取并执行里面的CMakeLists.txt
文件”。这通常用于将子项目或模块(比如第一周的示例代码)包含到主项目的构建流程中。之前定义的变量(如GLFW_LIB_DIR
)在子目录的CMakeLists.txt
中默认是可见和可用的。
- 作用: 指示 CMake 去处理指定子目录下的
现在要添加src来存放源文件和头文件,
OpenGL-for-Beginners/week_01_hello_window/src
然后将你之前下载解压的github项目,中第一个实例src目录下载的glad.c拷贝进来
现在,我们需要创建config类来包含头文件,当然其实也可以删除直接写在mian.cpp文件都是一样的。
Config.h
//
// Created by wuxianggujun on 2025/4/17.
//#pragma once#include <iostream>
#include <glad/glad.h>
#include <GLFW/glfw3.h>
然后再创建week_01_hello_window子模块的CMakeLists.txt文件。
OpenGL-for-Beginners/week_01_hello_window/CMakeLists.txt
并且编写如下代码
# 设置CMake最低版本要求
cmake_minimum_required(VERSION 3.10)
# 定义项目名称和版本 (项目名会被存入 ${PROJECT_NAME} 变量)
project(hello_window VERSION 1.0)# 创建可执行文件,指定源文件 (包括glad的实现文件)
add_executable(${PROJECT_NAME} src/glad.c src/main.cpp)# 为目标添加头文件搜索目录 (PRIVATE表示仅此目标需要)
# ${DEPENDENCIES_INCLUDE_DIR} 是上级目录定义的变量,指向依赖头文件目录
target_include_directories(${PROJECT_NAME} PRIVATE ${DEPENDENCIES_INCLUDE_DIR})# 链接GLFW动态库的导入库 (.lib)
# ${GLFW_LIB_DIR} 是上级目录定义的变量,指向依赖库文件目录
target_link_libraries(${PROJECT_NAME} PRIVATE "${GLFW_LIB_DIR}/glfw3dll.lib")# 判断是否为Windows平台
if(WIN32)# 在Windows上链接OpenGL库target_link_libraries(${PROJECT_NAME} PRIVATE opengl32)
endif()# --- 构建后复制 DLL 文件 ---
# 设置GLFW DLL文件的路径变量
set(GLFW_DLL_PATH "${GLFW_LIB_DIR}/glfw3.dll")# 检查DLL文件是否存在
if(EXISTS ${GLFW_DLL_PATH})# 添加自定义命令:在目标成功构建后执行add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD# 使用CMake的内置命令复制文件 (仅当文件不同或目标不存在时复制)COMMAND ${CMAKE_COMMAND} -E copy_if_different# 源文件: GLFW DLL"${GLFW_DLL_PATH}"# 目标目录: 可执行文件所在的目录 (<TARGET_FILE_DIR:...>是生成器表达式)$<TARGET_FILE_DIR:${PROJECT_NAME}># 在构建时显示的注释信息COMMENT "复制 glfw3.dll 到输出目录")
# 如果DLL文件不存在
else()# 输出警告信息,提示运行时可能出问题message(WARNING "未在 ${GLFW_DLL_PATH} 找到 glfw3.dll,运行时可能失败。")
endif()
接下来,终于是轮到创建main.cpp文件了。
OpenGL-for-Beginners/week_01_hello_window/src/main.cpp
//
// Created by wuxianggujun on 2025/4/18.
//
#include "Config.h" // 包含项目配置或所需头文件 (如 glad, glfw, iostream)
#include <iostream> // 为了 std::cout (如果 Config.h 没有包含)int main()
{GLFWwindow* window; // 声明窗口对象指针// 初始化 GLFWif (!glfwInit()){std::cout << "初始化 GLFW 失败" << std::endl; // 输出错误信息return -1; // 返回错误码}// 创建 GLFW 窗口window = glfwCreateWindow(640, 480, "Hello World", NULL, NULL);// (可选: 添加窗口创建失败的检查)// if (window == NULL) { ... glfwTerminate(); return -1; }// 设置当前线程的 OpenGL 上下文为刚创建的窗口glfwMakeContextCurrent(window);// 初始化 GLAD,加载 OpenGL 函数指针// 使用 glfwGetProcAddress 获取地址加载函数if (!gladLoadGLLoader(reinterpret_cast<GLADloadproc>(glfwGetProcAddress))){std::cout << "初始化 GLAD 失败" << std::endl; // 输出错误信息glfwTerminate(); // 清理 GLFWreturn -1; // 返回错误码}// 设置清屏颜色 (背景色 R, G, B, Alpha)glClearColor(0.25f, 0.5f, 0.75f, 1.0f);// 主循环/渲染循环,直到窗口关闭while (!glfwWindowShouldClose(window)){// 处理窗口事件 (键盘、鼠标、关闭按钮等)glfwPollEvents();// 清除颜色缓冲区 (使用 glClearColor 设置的颜色)glClear(GL_COLOR_BUFFER_BIT);// 交换前后缓冲区 (将后台绘制的内容显示到屏幕)glfwSwapBuffers(window);} // 循环结束// 清理并终止 GLFWglfwTerminate();return 0; // 程序正常退出
}
运行结果
运行之后,效果就是一个蓝色窗口
当点击右上角的关闭按钮之后会正常退出进程已结束,退出代码为 0
,那这就是没有问题的。我们的这个示例只是简单的使用GLFw搭建了一个窗口,同时再测试一下依赖库的配置是否正常。