小白的OpenGL3.3自学之路(2)OpenGL3.3之打开一个窗口
还记得上一篇那段测试代码吗?
那段代码就是最简单的应用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窗口每一个像素颜色值的大缓冲),它在这一迭代中被用来绘制,并且将会作为输出显示在屏幕上。说了一大堆。说白了就是应用双缓冲交换颜色缓冲。那么问题来了。什么是双缓冲技术?
感觉有点像以前的双排火枪手啊,第一排开枪,第二排把枪装好弹然后递给第一排
glfwTerminate()函数释放/删除之前分配的资源.
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
glViewport(0, 0, width, height);
}
glViewport函数前两个参数控制窗口左下角的位置。第三个和第四个参数控制渲染窗口的宽度和高度(像素)。