文章目录
- GLFW
- 获取 GLFW
- GLAD
- 获取 GLAD
- 在 Xcode 中配置下载好的 GLFW 和 GLAD
- 配置流程
- 检测是否配置成功
- 无关配置的题外话——Xcode 下安全的删除移动操作
GLFW
Graphics Library Framework(图形库框架),可以让我们通过其封装好的 通用API 来正确创建 OpenGL context(上下文)
并显示出一个简单的 窗口
。
如果没有这个跨平台工具,那么我们在 windows 上创建 OpenGL窗口 时需要调用显卡中提供的 windows 使用的 API,而在 Mac 上创建窗口时需要 MacOS 的 API。
获取 GLFW
从官网上下载GLFW macOS下64位二进制文件
GLAD
因为 OpenGL 只是一个标准/规范,驱动开发商针对不同显卡有不同的具体实现,导致 OpenGL 驱动版本过于庞杂,大多数函数的位置都无法在编译时确定下来,需要在运行时查询。 因此开发者需要在运行时获取函数地址并将其保存在一个函数指针中供以后使用。
这里来看一个 LearnOpenGL CN 中所举的 Windows 下获取函数地址的例子:
// 定义函数原型
typedef void (*GL_GENBUFFERS) (GLsizei, GLuint*);
// 找到正确的函数并赋值给函数指针
GL_GENBUFFERS glGenBuffers = (GL_GENBUFFERS)wglGetProcAddress("glGenBuffers");
// 现在函数可以被正常调用了
GLuint buffer;
glGenBuffers(1, &buffer);
对于开发人员而言,每使用一个函数就要经历这样的寻址过程无疑是极其痛苦的,因此需要通过 GLAD 库加载所有OpenGL函数指针,来简化使用函数的流程。
获取 GLAD
打开 GLAD 的在线服务:
- 将
Language
设置为C/C++
; - 在
API
选项中,选择3.3
以上的 OpenGL(gl)版本(更新的版本也能用); - 之后将
模式(Profile)
设置为Core
。Compatibility
兼容旧版本,包含低版本中的 API 。Core
只包含当前版本必须支持的 API ,不考虑向下兼容旧版本,更为轻巧。
- 选中
生成加载器(Generate a loader)
选项。 - 可以先(暂时)忽略
扩展(Extensions)
中的内容。 - 点击
生成(Generate)
按钮来生成库文件。
下载得到一个 zip
压缩文件,包含两个头文件目录,里面分别放着一个和目录同名的 .h
文件,和一个放着 glad.c
文件的 src
目录:
在 Xcode 中配置下载好的 GLFW 和 GLAD
配置流程
- 新建项目。
- 选择
Command Line Tool
。
- 项目名称随便起,这里我起名为
OpenGL
。
- 项目所在位置自己选择,不影响后续配置。
- 接下来通过如图所示流程配置 Header Search Paths(头文件搜索路径)。【当然,初次配置时 Header Search Paths 对应的路径为空,图中路径不为空是因为我配置好了】:
- 双击红框中标注的地方,从访达中拖拽下载好的
glad/include文件夹
与glfw-3.3.7.bin.MACOS/include文件夹
到弹出的方框中(对于这两个文件夹在访达中的所在位置,你可能与我不一样,因此按照你自己放置的路径来配置即可),配置头文件搜索路径:
拖动添加:
- 在
main.cpp
文件中添加以下代码,注意添加时两个 include 语句的顺序不能改变:
#include <glad/glad.h>
#include <GLFW/glfw3.h>
请确认是在包含 GLFW 的头文件之前包含了 GLAD 的头文件。GLAD 的头文件包含了正确的 OpenGL 头文件(例如GL/gl.h),所以需要在其它依赖于 OpenGL 的头文件之前包含 GLAD。
- 点击运行按钮(红框标注),提示构建成功,则表明头文件搜索路径添加成功,已经可以使用 GLFW 和 GLAD 的头文件:
- 根据下图顺序查看三个文件是否分别被添加进黄框所示部分,即可核验是否成功链接(Link)添加的文件
libglfw3.a
、libglfw3.dylib
(静态库、动态库两者添加其一即可),以及glad.c
是否参与编译:
- 最好加上这两个框架,Xcode有可能会报框架缺失的错误,点“+”搜索名字添加即可:
检测是否配置成功
将下面的代码复制到 main.cpp 中并运行,如果能弹出一个墨绿色的窗口即证明配置成功。
以下代码源于 LearnOpenGL CN的创建窗口 ,代码中注释不够细致,如果不懂可以阅读文档了解更多。
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>// 对窗口注册一个回调函数(Callback Function),它会在每次窗口大小被调整的时候被调用。
// 参数:window - 被改变大小的窗口,width、height-窗口的新维度。
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{// 改变视口大小的函数glViewport(0, 0, width, height);
}// 实现输入控制的函数
void processInput(GLFWwindow *window)
{// glfwGetKey两个参数:窗口,按键// 没有被按下返回 GLFW_PRESSstd::cout << "是否点击ESC?" << std::endl;std::cout << glfwGetKey(window, GLFW_KEY_ESCAPE) << std::endl;if(glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)// 被按下则将 WindowShouldClose 属性置为 true// 以便于关闭 渲染循环glfwSetWindowShouldClose(window, true);
}int main()
{glfwInit(); // 初始化GLFW// glfwWindowHint函数的第一个参数代表选项的名称// 第二个参数接受一个整型,用来设置这个选项的值// 将主版本号(Major)和次版本号(Minor)都设为3glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);// 使用的是核心模式(Core-profile)glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);// 参数依次为:宽、高、窗口的名称,显示器用于全屏模式,设为NULL是为窗口// 窗口的上下文为共享资源,NULL为不共享资源GLFWwindow* window = glfwCreateWindow(800, 600, "FirstWindow", NULL, NULL);if (window == NULL){std::cout << "Failed to create GLFW window" << std::endl;// 释放空间,防止内存溢出glfwTerminate();return -1;}// 创建完毕之后,需要让window的context成为当前线程的current contextglfwMakeContextCurrent(window);// glfwGetProcAddress是glfw提供的用来加载 系统相关的OpenGL函数指针地址 的函数// 用gladLoadGLLoader函数根据使用者的系统定义了正确的函数if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)){std::cout << "Failed to initialize GLAD" << std::endl;return -1;}// 告诉OpenGL渲染窗口的尺寸大小,即视口(Viewport)// 这样OpenGL才只能知道怎样根据窗口大小显示数据和坐标// 调用glViewport函数来设置窗口的维度(Dimension)// 前两个参数控制窗口左下角的位置。第三个和第四个参数控制渲染窗口的宽度和高度(像素)glViewport(0, 0, 800, 600);// 窗口大小改变时视口也要随之改变,这通过对窗口注册 framebuffer_size_callback 实现。// 它会在每次窗口大小被调整时调用glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);/* 渲染循环(Render Loop) */// glfwWindowShouldClose 检查一次GLFW是否被要求退出// 为true时渲染循环结束while(!glfwWindowShouldClose(window)){// 监测键盘输入processInput(window);/* 渲染 */// 状态设置函数,设置清空屏幕所用的颜色glClearColor(0.2f, 0.3f, 0.3f, 1.0f);// 状态使用函数,使用设定好的颜色来清除旧的颜色缓冲glClear(GL_COLOR_BUFFER_BIT);// 上面两种函数起到的作用也可以用 glClearBufferfv 来现实/*GLfloat color[] = {0.2, 0.3, 0.3, 1.0};glClearBufferfv(GL_COLOR, 0, color);*/// glfwSwapBuffers 交换颜色缓冲,用来绘制并作为输出显示在屏幕glfwSwapBuffers(window);// glfwPollEvents 检查是否有触发事件glfwPollEvents();}glfwTerminate();return 0;
}
运行结果:
无关配置的题外话——Xcode 下安全的删除移动操作
接下来要讲的一点与配置关系不大,但是也给初使用 Xcode 的我带来了不小的麻烦。不同于 Windows 下的操作习惯,在 macOS 中尽量在访达中对文件进行移动、删除等操作,而非直接拖拽到 Xcode 的树形目录中进行移动,亦或直接在树形目录中进行删除。举个例子:
- 比如在当前项目(OpenGL)的同级目录下我新建了一个
test
文件夹,并且其中有文件test.sh
:
- 而后我将它通过访达拖拽移动到 OpenGL 目录下:
- 此时如果通过 Xcode 树形目录进行删除:
- 会询问删除方式,两种方式的区别详见本篇文章:
- Move to Trash:删除文件内容到废纸篓(此时的文件内容不在工程中,但是文件夹还是会在工程中):
删除前:
删除后:
- Reremove reference:移除文件的引用(只是删除引用,文件内容都还在工程中),后续如果添加相同文件会弹出“要添加的文件已存在”的报错提示,需要在访达中将上次仅删除引用的文件完全删除。
删除前同上。
删除后:
- 不论是
Reremove reference
还是Move to Trash
他们的文件夹都会在工程中(如果没有清除,下次添加重名的工程文件会弹出警告)。
PS:还遇到过删除 OpenGL 目录下的 test 文件夹,结果导致与 OpenGL 同级的 test 文件夹也被删除的情况,但后来无法复现,因此在这里无法贴图证明。总而言之,对 macOS 使用尚不熟练时,尽量使用访达对文件进行移动删除操作(通过拖拽获得文件路径是简单且安全的方法,可以使用),即使是对 Xcode 树形目录进行操作也请尽可能通过 Show in Finder
跳转到访达中,以避免出现不必要的错误与文件损失。