macOS平台OpenGL + GLEW + GLFW的正确初始化顺序

在之前博文中有写到如何在macOS平台搭建OpenGL的开发环境,但是平台搭建好之后,如果按照红皮书(第8版)上的代码来开始写自己的第一个OpenGL程序时,由于教程中使用的还是glut而不是glfw,所以程序相对要有些变化, 但是变化也不是随便变化的,其中有一些很重要的部分,尤其是初始化顺序,要特别注意

给出一个可以正常运行的初始化的例子( 在这者之前遇到了各种段错误崩溃)

int main(int argc, const char **arg)
{
    if(!glfwInit()) {
        return -1;
    }  

    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);

    auto window = glfwCreateWindow(1024, 768, "OpenGL Test", nullptr, nullptr);    
    if(!window) {
        glfwTerminate();
        return -1;
    }    
    glfwMakeContextCurrent(window);
    glfwSetWindowSizeCallback(window, resizeWindow);
    glfwSetKeyCallback(window,  keyEvent);


//    glewExperimental = GL_TRUE;
    GLuint code = glewInit();
    if(code != GLEW_OK)
    {
        std::cout << "GLEW Init Failed for reason: "  <<  glewGetErrorString(code)<< std::endl;
        return 1;
    }
    initGL();
    while (!glfwWindowShouldClose(window)) {
        /* Render here */

        paintGL();
        /* Swap front and back buffers */
        glfwSwapBuffers(window);

        /* Poll for and process events */
        glfwPollEvents();
    }

    glfwTerminate();
    return 0;
}

大致的初始化顺序为

1️⃣ 初始化glfw

if(!glfwInit()) {
    return -1;
}    

2️⃣ 为glfw设置一些不能缺少的重要参数

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);

3️⃣ 创建OpenGL窗体

auto window = glfwCreateWindow(1024, 768, 
                "OpenGL Test", nullptr, nullptr);    
if(!window) {
    glfwTerminate();
    return -1;
} 

4️⃣ 将上一步创建成功的窗体设置为OpenGL的Context

glfwMakeContextCurrent(window); // 设置窗体到当前的OpenGL Context
glfwSetWindowSizeCallback(window, resizeWindow); // 设置窗体尺寸改变的回调, 可选
glfwSetKeyCallback(window,  keyEvent); //设置按键处理回调,可选

5️⃣ 初始化glew

// glewExperimental = GL_TRUE;
GLuint code = glewInit();
if(code != GLEW_OK)
{
    std::cout << "GLEW Init Failed for reason: "  <<  glewGetErrorString(code)<< std::endl;
    return 1;
}

其中

// glewExperimental = GL_TRUE;

在不同平台会有不同的差异,Ubuntu环境下可能需要加上,笔者测试macOS环境下加不加这一句都没什么大的区别,因此在此处不做特别要求
6️⃣ 用户代码的OpenGL渲染初始化

initGL();

这里是由用户自己定义的初始化渲染环境的代码,具体可以参考《OpenGL编程指南(第8版)》的第一章的第一个OpenGL程序

7️⃣ glfw事件循环

while (!glfwWindowShouldClose(window)) {
    /* Render here */

    paintGL();
    /* Swap front and back buffers */
    glfwSwapBuffers(window);

    /* Poll for and process events */
    glfwPollEvents();
}

这里就是glfw和glut最大的区别, glut是设置显示回调函数后调用glutMainLoop(); 来启动事件循环, 而此处的glfw却是有个用户自己定义循环,而循环的结束标志是由glfw提供的api

glfwWindowShouldClose(GLFWwindow *window);

来判断窗体是否关闭

paintGL();

相当于glut设置的显示回调, 此处由用户自己在事件循环中主动调用

glfwSwapBuffers(GLFWwindow *window);

此处调用表示交换前后台缓冲区(使用双缓冲技术时会用到, 默认情况下会开启创缓冲技术)

glfwPollEvents();

这里是用来更新窗体事件的,主要用来搜集系统产生的各种事件,然后派发至用户自定义的各种事件回调中,如:

glfwSetWindowSizeCallback(window, resizeWindow); // 设置窗体尺寸改变的回调
glfwSetKeyCallback(window,  keyEvent); //设置按键处理回调,可选

这两行注册的两个回调就会被调用

只有按正确的初始化顺序执行的OpenGL程序才能正常运行,否则在程序启动之后,要么会因为初始化失败之后的判断退出程序,要么会应为没有进行合理的判断导致程序出现段错误而崩溃掉,这些都是很让人头疼的事。

因此,笔者在此篇博文中详细的为大家说明了各个重要环节的执行顺序以及其含义,希望后来的初学者尽量不要在相同的地方栽跟头(一大堆未知的段错误)

你可能感兴趣的:(opengl,C++,图形,OpenGL,GLEWGLFW)