使用 Xcode 配置 OpenGL 开发环境

前提准备

在打开 Xcode 之前,我们需要准备两个库: GLFW
GLAD
。这是开发 OpenGL 需要依赖的库,所以先看看如何准备好这两个库吧:

GLFW

在我们画出任何效果之前,首先要做的就是创建一个OpenGL上下文和一个用于显示的窗口。然而,这些操作在每个系统上都是不一样的,OpenGL有目的地将这些操作抽象出去。这意味着我们不得不自己处理创建窗口,定义OpenGL上下文以及处理用户输入。
幸运的是,有一些库已经提供了我们所需的功能,其中一部分是特别针对OpenGL的。这些库节省了我们书写操作系统相关代码的时间,提供给我们一个窗口和上下文用来渲染。最流行的几个库有GLUT,SDL,SFML和GLFW。
现在我们就 GLFW 官方上下载这个库 :https://www.glfw.org/download.html
你可以选择下载源码来编译,或者是直接下载预编译的版本。这里为了方便,我就直接下载的是 macOS 的预编译的库:


下载完成之后,将其解压并打开。我们需要里面的这些内容:

  • 编译生成的库
  • include文件夹

即下面两个文件夹中的内容:


我们在后面会看到怎么使用这两个文件夹。现在 GLFW 已经准备好了,接下来,准备GLAD这个库。

GLAD

因为OpenGL只是一个标准/规范,具体的实现是由驱动开发商针对特定显卡实现的。由于OpenGL驱动版本众多,它大多数函数的位置都无法在编译时确定下来,需要在运行时查询。所以任务就落在了开发者身上,开发者需要在运行时获取函数地址并将其保存在一个函数指针中供以后使用。这样的代码非常复杂,而且很繁琐,我们需要对每个可能使用的函数都要重复这个过程。幸运的是,GLAD这可库可以帮助我们做这些事情。
进入 GLAD 的官方网站:https://glad.dav1d.de/
将语言(Language)设置为C/C++,在API选项中,选择3.3的OpenGL版本,之后将模式(Profile)设置为Core,并且保证生成加载器(Generate
a
loader)的选项是选中的。现在可以先(暂时)忽略拓展(Extensions)中的内容。都选择完之后,点击生成(Generate)按钮来生成库文件:


在下载完成之后,会有两个文件夹,这两个文件夹后续都需要用到的:


在准备完成之后,下面就需要打开 Xcode 进行配置了。

打开 Xcode

创建新项目

现在新建一个 Xcode 项目,这里选择控制台程序就行:


下一步中的内容随便填一填就行了,这里我的语言就设置为 C++ :

添加依赖项

添加头文件搜索路劲

首先,我们需要把头文件加入到工程中:

这个设置在
xcodeproj -> Build Settings -> Search Paths -> Header Search Paths

,我们需要在这里添加刚才下载的那两个库的头文件,也就是解压后的 include 文件夹。

添加 glad.c 文件

其次,还记得下载完 glad 中有一个 src 文件夹么,我们需要将其中的文件 glad.c 文件添加到工程中:

添加 glfw 等依赖库

然后,我们还需要加入 glfw 的库文件,并且,加入其它的几个库:

这个设置的路径在
xcodeproj -> Build Phases -> Link Binary With Libraries

,这里添加的
libglfw3.a

,就是我们刚才下载的 glfw 里面的内容,这个需要以 add File 的方式引入,其它的几个也需要引入,搜索就能找到。

添加库文件搜索路劲

最后,还需要添加库文件的搜索路劲,否则 Xcode 是找不到 libglfw3.a
这个文件的。添加这个路径与添加头文件搜索路劲一样,这个设置是在 xcodeproj -> Build Settings -> Search Paths -> Library Search Paths


通过这个几个步骤,你现在就可以使用 OpenGL 进行绘制工作了。下面,我们就来画一个三角形吧。

配置完成,画一个三角形

通过 OpenGL 绘制一个三角形并不是一个简单的工作,不仅要调用 GLFW 创建 OpenGL 上下文,还要编译着色器,构建绘制程序,还要处理顶点数组对象等等。这不是一时半会能说完的,此处就多说。

最后,附上一个程序,将这个程序复制到 Xcode 的
main.cpp

中,运行一下,如果能出来一张三角形,那么,Xcode 的 OpenGL 的环境就已经算是配置成功了,最终结果如下图:


代码如下:

#include 
#include


#include


void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
glViewport(0, 0, width, height);
}




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

glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);

GLFWwindow* window = glfwCreateWindow(800, 600, "example", NULL, NULL);
if (window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);

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


glViewport(0, 0, 800, 600);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);

const char *vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"void main()\n"
"{\n"
" gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
"}\0";

unsigned int vertexShader;
vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
glCompileShader(vertexShader);

int success;
char infoLog[512];
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
if(!success) {
glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
}

const char *fragmentShaderSource = "#version 330 core\n"
"out vec4 FragColor;\n"
"void main()\n"
"{\n"
" FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"
"}\n\0";

unsigned int fragmentShader;
fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);

glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
if(!success) {
glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
}

unsigned int shaderProgram;
shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);

glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
if(!success) {
glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
std::cout << "ERROR::PROGRAM::LINK_FAILED\n" << infoLog << std::endl;
}

glUseProgram(shaderProgram);
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);

unsigned int VAO;
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);

float vertices[] = {
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
0.0f, 0.5f, 0.0f
};

unsigned int VBO;
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);

glClearColor(0.2f, 0.3f, 0.3f, 1.0f);

while(!glfwWindowShouldClose(window))
{
glClear(GL_COLOR_BUFFER_BIT);

glDrawArrays(GL_TRIANGLES, 0, 3);

glfwSwapBuffers(window);
glfwPollEvents();
}

glfwTerminate();
return 0;
}