在学习 Hazel 游戏引程的过程中,第十四集介绍了一个非常核心的内容:
为什么我们需要现代 OpenGL,以及为什么必须引入 Glad 这样的 OpenGL 函数加载器。
这一集内容看似简单,但实际上涉及图形 API 的底层机制,是理解后续渲染系统的基础。
本文将从以下几个角度详细展开:
什么是“现代 OpenGL”
Windows 或 macOS 里为什么不能直接使用 OpenGL 函数?
什么是 Glad,为什么必须使用 Glad?
Glad 是如何加载 OpenGL 的?
Hazel 项目中如何整合 Glad?(含完整 Premake 配置)
初始化 Glad 的流程与渲染循环示例
常见错误与排查方法
我的理解与总结
01. 什么是“现代 OpenGL”?
OpenGL 是一个历史非常久的图形 API。
为了兼容古老的渲染方式,OpenGL 里面其实有两种功能:
旧版固定管线(Fixed Pipeline)
例如:
glBegin()
glEnd()
glMatrixMode()
glVertex3f()
这些接口从 OpenGL 3.0 起就被废弃(deprecated),并在 3.3 Core Profile 中完全移除。
现代可编程管线(Programmable Pipeline)
使用:
顶点缓冲区(VBO)
顶点数组对象(VAO)
着色器(Shader)
帧缓冲(FBO)
Hazel 引擎要使用的就是 OpenGL 3.3+ Core Profile。
这意味着:
Hazel 引擎完全依赖现代 OpenGL,不使用任何旧式接口。
02. 操作系统为什么不直接提供现代 OpenGL 函数?
这是初学者最容易疑惑的问题。
比如你想直接调用:
glGenVertexArrays();
glBindBuffer();
glCompileShader();
你会发现系统里找不到这些函数!
原因是:
Windows / macOS 默认只提供了 OpenGL 1.1(Windows)或 2.1(macOS) 的函数。
现代 OpenGL 是通过 GPU 驱动提供的,而不是系统提供。
所以:
操作系统并不知道 glGenVertexArrays 等函数的地址
必须由 GPU 驱动提供
而你必须通过 函数加载器(Loader) 把它们的真实地址“取出来”
这就是为什么必须使用 GLAD。
03. Glad 是什么?为什么 Hazel 要用它?
Glad 是一个 OpenGL 函数加载器(Loader)生成器。
它的作用是:
1. 查询 GPU 是否支持某个 OpenGL 版本
你需要 OpenGL 3.3,那 Glad 会告诉你驱动是否支持。
2. 把所有现代 OpenGL 函数的“真实地址”从驱动里取出来
例如 glGenBuffers() 在 GPU 驱动内部的地址可能是:
0x7FFD12AF3C90
Glad 会在初始化时调用:
gladLoadGLLoader((GLADloadproc)glfwGetProcAddress);
然后自动把所有函数地址写入到打开的 glXXX() 函数指针中。
3. 没有加载器,OpenGL 根本没法用
你不能直接:
glGenBuffers();
因为程序找不到这个函数。
所以你必须:
先创建 OpenGL 上下文(由 GLFW 完成)
再使用 Glad 从驱动里加载函数
最终你才能使用现代 OpenGL。
04. Hazel 里 Glad 的工作流程(非常重要)
Hazel 的初始化顺序如下:
1. 创建窗口(GLFW)
窗口创建时,会创建一个 OpenGL context(上下文)。
2. 让 context 变成当前线程的上下文
glfwMakeContextCurrent(window);
3. 加载 OpenGL 函数(通过 Glad)
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
std::cout << "Failed to initialize Glad!" << std::endl;
}
4. 现在你才能使用任何现代 OpenGL 函数
例如:
glCreateShader(GL_VERTEX_SHADER);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glClear(GL_COLOR_BUFFER_BIT);
这 4 个步骤是现代 OpenGL 的基础。
无法跳过任何一步。
05. Hazel 是如何整合 Glad 的?(premake 配置)
Hazel/vendor/Glad/premake5.lua
project "Glad"
kind "StaticLib"
language "C"
targetdir ("bin/" .. outputdir .. "/%{prj.name}")
objdir ("bin-int/" .. outputdir .. "/%{prj.name}")
files
{
"include/glad/glad.h",
"include/KHR/khrplatform.h",
"src/glad.c"
}
includedirs
{
"include"
}
filter "system:windows"
staticruntime "On"
systemversion "latest"
Hazel/premake5.lua 中链接 Glad
IncludeDir["Glad"] = "Hazel/vendor/Glad/include"
includedirs
{
"%{IncludeDir.GLFW}",
"%{IncludeDir.Glad}"
}
links
{
"GLFW",
"Glad",
"opengl32.lib"
}
defines
{
"GLFW_INCLUDE_NONE"
}
注意:GLFW_INCLUDE_NONE 必须写,否则 GLFW 会自动包含系统 gl.h,与 Glad 冲突。
06. Glad 的初始化代码(Hazel Application)
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
HZ_CORE_ERROR("Failed to initialize Glad!");
}
这是整个渲染系统的入口点。
之后 Hazel 才能调用:
glClearColor(0.1f, 0.1f, 0.1f, 1);
glClear(GL_COLOR_BUFFER_BIT);
07. 常见错误 & 排查
错误 1:找不到 glXXX 函数
原因:忘了写这句:
gladLoadGLLoader((GLADloadproc)glfwGetProcAddress);
错误 2:GLFW 自动包含了 gl.h
原因:缺少:
"#define GLFW_INCLUDE_NONE"
解决:在 premake 中加入:
defines { "GLFW_INCLUDE_NONE" }
错误 3:Glad 头文件路径写错
你的错误示例(你之前的写法):
include/glad.glad.h
正确:
include/glad/glad.h
错误 4:Glad 静态库 runtime 设置拼写错误
必须写:
staticruntime "On"
08. 我的理解与总结
通过这一节,我彻底理解了为什么 Windows/macOS 下无法直接使用现代 OpenGL。
关键原因是:
系统只提供极老版本的 OpenGL,现代函数全部存在于 GPU 驱动里,因此必须由 Glad 这种“函数加载器”把它们加载出来。
Hazel 的 OpenGL 初始化过程非常标准:
GLFW 创建窗口 → 创建 OpenGL 上下文
Glad 从驱动加载 OpenGL 函数
Hazel 才能开始渲染
这也是所有现代游戏引擎必须遵循的流程。
理解 Glad 之后,我终于知道为什么教材里总是有:
gladLoadGLLoader();
以前我以为这是“固定写法”,但实际上它是现代 OpenGL 能正常运行的关键。
这一集虽然看似“知识点不多”,但却是之后所有渲染代码的基础,比如:
Vertex Buffer
Index Buffer
Shader Program
Texture
Framebuffer
Render Command
这些都必须依赖 Glad 才能使用。