欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

小白的OpenGL3.3自学之路(2)OpenGL3.3之打开一个窗口

程序员文章站 2024-03-25 16:54:28
...

还记得上一篇那段测试代码吗?

那段代码就是最简单的应用glfw打开一个窗口,代码如下:

#include <glad/glad.h>
#include <GLFW/glfw3.h>

#include <iostream>

void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow *window);

const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;

int main()
{
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

    GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
    if (window == NULL)
    {
        std::cout << "Failed to create GLFW window" << std::endl;
        glfwTerminate();
        return -1;
    }
    glfwMakeContextCurrent(window);
    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);

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

    while (!glfwWindowShouldClose(window))
    {
        processInput(window);
        glfwPollEvents();

        glfwSwapBuffers(window);
    }

    glfwTerminate();
    return 0;
}

void processInput(GLFWwindow *window)
{
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
        glfwSetWindowShouldClose(window, true);
}

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

下面本小白开始对这段代码进行简单的了解:

#include <glad/glad.h>
#include <GLFW/glfw3.h>

这里是头文件的包含。要切记先包含GLAD的头文件,再包含其他依赖于OpenGL的头文件。

void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow *window);

const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;

framebuffer_size_callback函数是对窗口注册一个回调函数,在窗口大小被调整时调用。
processInput函数是在GLFW中实现一些输入控制。
剩下的就是初始化窗口的宽度为800,高度为60。

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

glfwInit();->初始化GLFW
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR,3);->OpenGL主版本号(Major)为3
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR,3);->OpenGL次版本号(Minor)为3
glfwWindowHint(GLFW_OPENGL_PROFILE,GLFW_OPENGLCORE_PROFILE);->OpenGL使用核心模式
以上告诉了我们要使用的OpenGL版本是3.3,并且是使用核心模式,也就是可编程管线模式。

GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
if (window == NULL)
{
    std::cout << "Failed to create GLFW window" << std::endl;
    glfwTerminate();
    return -1;
}
glfwMakeContextCurrent(window);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);

第一句创建一个窗口对象,用来存放和窗口相关的数据,比如长,宽,窗口标题。
glfwMakeContextCurrent(window);在使用OpenGL API之前,要设置窗口对象作为当前的上下文。这个上下文将会一直被保持,直到你设置了另一个上下文或者拥有当前上下文的窗口被销毁。
那么问题来了。究竟什么是上下文,为什么每次渲染之前都要先设置上下文。能不能不要上下文?

OpenGL状态机

要解决这个问题,需要知道为什么说OpenGL是一个状态机。
那什么是状态机呢?状态机描述了一个对象在其生命周期内所经历的各种状态,状态的改变,为什么发生改变等。这么说还是云里雾里。那直接说状态机的特点吧。
1.有记忆功能,能记住其当前的状态
2.可以接收输入,根据输入的内容和自己的原先状态,修改自己当前状态,并且可以有对应输出
3.当进入特殊状态的时候,便不再接收输入,停止工作。
那么类比到OpenGL有如下理解:
1.OpenGL可以记录自己的状态。比如设置图元类型为三角形,颜色分量,纹理坐标等。
2.OpenGL可以通过调用函数来改变自己的状态。
3.OpenGL中当glfwWindowShouldClose(window)返回值为true时,就不再继续渲染,直接退出。
那么理解了状态机之后,这些状态机里面保存的跟渲染有关的状态,就是上下文,那么很自然地想到我们要在渲染之前先设置上下文,这个上下文记录了OpenGL渲染所需要的所有信息,可以类比C语言里面的结构体,这个结构体里面存储了当前绘制所使用的颜色,是否有光照计算,是否开启光源等状态属性,从底层的角度看,上下文就是在实际渲染前,驱动为寄存器所做的配置,有了上下文,驱动下达命令,GPU管线根据之前记录的状态来处理数据。
那么返回上面的代码:glfwMakeContextCurrent(window);这句话的意思是把之前创建的窗口设置为当前线程的上下文。那么以后更改状态后比如颜色等,也就会第一时间显示到窗口。

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

GLAD是用来管理OpenGL的函数指针的,所以在调用任何OpenGL的函数之前我们需要初始化GLAD。

while (!glfwWindowShouldClose(window))
    {
        processInput(window);
        glfwPollEvents();
        glfwSwapBuffers(window);
    }
void processInput(GLFWwindow *window)
{
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
        glfwSetWindowShouldClose(window, true);
}

如果glfwWindowShouldClose函数返回值为false,那么不会关闭窗口。就进入processInput函数,如果没有按下Esc键,那么glfwWindowShouldClose(window)继续为false,意思就是继续绘制。glfwPollEvents()这个函数检查有没有触发什么事件(比如键盘输入、鼠标移动等)。就到了glfwSwapBuffers(window);这个函数会交换颜色缓冲(它是一个储存着GLFW窗口每一个像素颜色值的大缓冲),它在这一迭代中被用来绘制,并且将会作为输出显示在屏幕上。说了一大堆。说白了就是应用双缓冲交换颜色缓冲。那么问题来了。什么是双缓冲技术?
小白的OpenGL3.3自学之路(2)OpenGL3.3之打开一个窗口
感觉有点像以前的双排火枪手啊,第一排开枪,第二排把枪装好弹然后递给第一排
glfwTerminate()函数释放/删除之前分配的资源.

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

glViewport函数前两个参数控制窗口左下角的位置。第三个和第四个参数控制渲染窗口的宽度和高度(像素)。

相关标签: OpenGL