一、前三章节的前情回顾
章节一:上下文(Context)
OpenGL学习笔记【1】——简介-CSDN博客
章节一讲述了OpenGL在渲染的时候需要一个Context来记录了OpenGL渲染需要的所有信息和状态,可以把上下文理解成一个大的结构体,它里面记录了当前绘制使用的颜色、是否有光照计算以及开启的光源等。不同的操作系统,都有各自的上下文创建方法,最简单的上下文可以通过GLFW创建。
章节二:GLFW库
OpenGL学习笔记【2】——开发环境配置(GLFW,VS,Cmake),创建第一个项目-CSDN博客
章节二讲述了一个专门的窗口库:GLFW库,一个轻量级的图形界面框架,GLFW 的主要功能是创建并管理窗口和 OpenGL 上下文,同时还提供了处理手柄、键盘、鼠标输入的功能。
章节二还创建了一个空项目MyFirstOpenGL。
章节三: GLAD库
OpenGL学习笔记【3】—— GLAD配置-CSDN博客
章节三讲述了GLAD库是用来管理OpenGL的函数指针的,所以在调用任何OpenGL的函数之前我们需要初始化GLAD,从而让我们能够使用所有OpenGL函数。
章节三还创建了一个main.cpp文件,代码中引入了GLAD和GLFW的两个库文件。
前三章节准备工作已经完成,接下来可以开始创建窗口了
二、创建窗口步骤
2.1、初始化GLFW库
glfwInit()函数: 一般我们使用Glfw库,首先初始化GLFW库,即需要调用glfwInit(),得到OpenGL随显卡驱动一起发布的新特性的函数入口地址。
2.2、 glfwWindowHint配置GLFW
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE)
使用核心模式(Core-profile),在调用一个OpenGL的废弃函数时会产生invalid operation 错误,当意外的使用了不该使用的旧函数时是一个很好的提醒。
注意:请确认您的系统支持OpenGL3.3或更高版本,否则此应用有可能会崩溃或者出现不可预知的错误,如果你的OpenGL版本低于3.3,检查一下显卡是否支持OpenGL 3.3+(不支持的话你的显卡真的太老了),并更新你的驱动程序,有必要的话请更新显卡。
2.3、创建一个窗口对象
(1) glfwCreateWindow()函数:创建窗口对象 返回值类型:GLFWwindow*
参数1,2为窗口的宽度和高度,参数3是窗口的名字,最后两个参数可以忽略。
glfwCreateWindow
创建了一个宽度为800像素、高度为600像素的窗口。
注意: 其实处理过的的窗口值是从-1到1的显示,而(0,0)
点就是窗口的中心。
(2) glfwMakeContextCurrent(window)函数:
将窗口window设置为当前线程的主上下文,即捕获当前窗口,准备对当前窗口进行画图.
(3) glfwTerminate(); 终止GLFW,释放资源。
2.4、渲染循环
为了在主动关闭GLFW之前可以不断绘制,在OpenGL中需要添加一个while循环,我们可以把它称之为渲染循环(Render Loop)。如果没有渲染循环,程序运行的话渲染效果只会出现一次就会快速消失。
glfwWindowShouldClose(window)函数:检查GLFW是否被要求退出。
2.5、点击本地Windows调试器运行测试
运行结果如下就表示窗口创建成功了
2.6、窗口创建成功,接下来给窗口添加渲染颜色
上面创建的窗口是白色的,我们给这个窗口上个颜色,但是给窗口上颜色之前,需要先初始化GLAD。
(1)初始化GLAD
因为用来给窗口上色的相关函数是openGL的函数,由第三章节对于GLAD的介绍我们已经知道在调用任何OpenGL的函数之前需要先初始化GLAD,初始化后我们才能够使用所有的OpenGL函数。
//初始化GLAD
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
//初始化失败,即加载函数地址失败,打印错误信息
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
gladLoadGLLoader(GLADloadproc)函数:根据我们编译的系统,GLAD库中用于加载系统相关的OpenGL函数指针地址。参数1为GLADloadproc类型的函数指针地址。这里我们通过GLFW中的glfwGetProcAddress属性获取了函数指针的的地址。
初始化GLAD后,接下来就能够使用所有OpenGL函数 了,即可以给窗口上色了。
(2) 给窗口添加渲染颜色(给窗口上色)
我们要把所有的渲染(Rendering)操作放到渲染循环中,因为我们想让这些渲染指令在每次渲染循环迭代的时候都能被执行。
既然渲染指令放在渲染循环里,渲染指令又是OpenGL函数,所以说初始化GLAD函数要放在渲染循环代码的前面。
while (!glfwWindowShouldClose(window))
{
//设置窗口颜色为黑色(参数1,2,3,4对应:R,G,B,A)
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT); //清空颜色缓冲
glfwSwapBuffers(window);
}
glClearColor()函数:清空当前窗口的所有颜色,并且给窗口设置成指定参数的背景色;
glClear(GL_COLOR_BUFFER_BIT)函数:将缓存清除为预先的设置值(设置窗口为黑色);
glClear传入的参数是:缓冲位(Buffer Bit),这里我们只关心颜色值,所以只清空颜色缓冲即可。
颜色缓冲(GL_COLOR_BUFFER_BIT):这个缓冲区更新存储颜色数据;
深度缓冲(GL_DEPTH_BUFFER_BIT):这个缓冲区存储顶点深度值;
模板缓冲(GL_STENCIL_BUFFER_BIT):用来做模板测试。模板缓冲类似于深度缓冲。模板测试的结果决定了像素的颜色值是否要被写入到渲染目标,像素的深度值是否要被写入深度缓冲。
glClearColor函数是一个设置函数,而glClear函数是一个使用的函数,glClear使用了当前设置的颜色给窗口上色。
glfwSwapBuffers(window)函数:交换(Swap)前缓冲和后缓冲.
为什么要交换缓冲?
因为电脑绘图是一个个像素逐一画的,需要时间,如果单一缓冲,我们可能会看到具体绘画过程,会造成屏幕闪烁等问题,所以为了解决这个问题,这里用了双缓冲技术,用两个内存区域来保存数据,分为前缓冲区和后缓冲区,前缓冲区用于展示屏幕上的内容,而后缓冲区就用来绘制,然后每一帧开始的时候,将两个缓冲区交换,这样后缓冲区又可以画新的内容。
单缓冲和双缓冲的区别?
单缓冲:是将所有的绘图指令在窗口上执行,就是直接在窗口上绘图,这样的绘图效率是比较慢的,如果使用单缓冲,而电脑比较慢,你回看到屏幕的闪烁。
双缓冲:实际上的绘图指令是在一个缓冲区完成,这里的绘图非常的快,在绘图指令完成之后,再通过交换指令(glfwSwapBuffers(window))把完成的图形立即显示在屏幕上,这就避免了出现绘图的不完整,同时效率很高。 一般用OpenGL绘图都是用双缓冲,单缓冲一般只用于显示单独的一副非动态的图像。
点击运行,查看给窗口上色的效果:
三、按下ESC键可以关闭GLFW窗口
通过章节二我们知道GLFW库提供了处理手柄、键盘、鼠标输入的功能,接下来我们就实现按下Esc键关闭我们创建的窗口。
(1)编写processInput函数
注意:processInput函数一定要放在main函数前面
//监听键盘输入函数
void processInput(GLFWwindow* window) {
//按下ESC键,关闭指定窗口
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
{
glfwSetWindowShouldClose(window, true);
}
}
glfwGetKey(参数1,参数2):
参数1:响应窗口对象;参数2:按下的键;返回值:int。
glfwSetWindowShouldClose(参数1,参数2):
参数1:响应窗口对象,参数2:用来设置是否关闭窗口。
(2)调用processInput函数
glfwPollEvents()函数的作用是处理并接收窗口事件,例如鼠标移动、键盘输入等。通常,该函数需要在每一帧渲染循环中被调用,以便及时地接收并处理用户的输入。
四、释放/删除之前分配的所有资源
//终止GLFW,释放所有资源====================
glfwTerminate();
此章节的代码如下所示:
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>// C++的标准输入输出头文件
//监听键盘输入函数
void processInput(GLFWwindow* window) {
//按下ESC键,关闭指定窗口
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
{
glfwSetWindowShouldClose(window, true);
}
}
int main()
{
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);//设置主板本号为3
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);//设置副版本号为3
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
//创建窗口对象 返回值类型:GLFWwindow*
GLFWwindow* window = glfwCreateWindow(800, 600, "LearnOpenGL", NULL, NULL);
if (window == NULL)//创建窗口对象失败,打印错误信息
{
std::cout << "Failed to create GLFW window" << std::endl;//需要引用iostream头文件
glfwTerminate(); //终止GLFW
return -1;
}
glfwMakeContextCurrent(window); //将窗口的上下文设置为当前线程的主上下文
//初始化GLAD
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
//初始化失败,即加载函数地址失败,打印错误信息
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
//渲染循环
while (!glfwWindowShouldClose(window))
{
//设置窗口颜色为黑色(参数1,2,3,4对应:R,G,B,A)
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT); //清空颜色缓冲
glfwSwapBuffers(window);
//检查有没有触发什么事件(比如键盘输入、鼠标移动等)、更新窗口状态
glfwPollEvents();
//调用按下Esc键,关闭窗口
processInput(window);
}
//终止GLFW,释放所有资源====================
glfwTerminate();
return 0;
}