Loading

OpenGL(一) 渲染循环创建窗口

OpenGL(一) 渲染循环创建窗口

注:本文仅供自己以及OPENGL初学者共同学习进步,并无实际教学意义,不过会对自己学习中遇到的关键点加上个人解释。应该会对初学者有所帮助,如有错误请指正!

https://jveilcoo.github.io/XQ.github.io/categories/ 观看更清晰
上次我们已经配置好了glfw + glad环境,接下来就是我们实践使用的时候了。


首先,我们先将要使用的头文件加进来,注意,这里先后顺序不能错:

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

然后我们开始进入main()函数,初始化glfw,并且声明版本号:

int main()
{
	glfwInit(); //初始化glfw
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); //版本号
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); //代表glfw3.3
}

我这里使用的是3.3版本。
然后我们用glfwCreateWindow()函数创建一个窗口对象:

int main()
{
	glfwInit(); //初始化glfw
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); //版本号
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); //代表glfw3.3

	//创建窗口
	GLFWwindow * window = glfwCreateWindow(800, 600, "LearnOpenGL", NULL, NULL);
	//判断是否创建成功
	if (window == NULL)
	{
		std::cout << "Failed to create GLFW window" << std::endl;
		glfwTerminate();
		return -1;
	}
    glfwMakeContextCurrent(window); //设置当前线程上下文 意思就是在切换下一个状态之前,进行的所有操作都是对 window这个对象进行的
}

在源码注释里,GLFWwindow是"brief Opaque window object",也就是简易窗口对象的意思。
glfwCreateWindow()函数显而易见,第一个参数是宽度,第二个参数是高度,第三个是title,也就是窗口名称,后面两个参数现在用不到。当窗口创建失败时,window指针会指向NULL,我们可以利用这个来判断窗口是否创建成功。

glfwTerminate()是释放资源的函数,当一切结束的时候,我们就要用到这个函数。
glfwMakeContextCurrent(window)就如注释所说,OPENGL是讲状态的,只要我们不在当前线程设置其他上下文,它就会一直绑定当前设置的window窗口,之后的所有操作,就都是在这个window里进行.


接下来我们初始化glad:

int main()
{
	glfwInit(); //初始化glfw
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); //版本号
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); //代表glfw3.3

	//创建窗口
	GLFWwindow * window = glfwCreateWindow(800, 600, "LearnOpenGL", NULL, NULL);
	//判断是否创建成功
	if (window == NULL)
	{
		std::cout << "Failed to create GLFW window" << std::endl;
		glfwTerminate();
		return -1;
	}
    glfwMakeContextCurrent(window); //设置当前线程上下文 意思就是在切换下一个状态之前,进行的所有操作都是对 window这个对象进行的

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

    //开始渲染之前先告诉opengl渲染窗口的尺寸大小,以及坐标
	//注意,opengl坐标范围为-1到1 ,与屏幕坐标之间存 映射关系
	glViewport(0, 0, 800, 600); 
	//注册该函数,告诉opengl每次window调整大小时都调用该函数
	glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); 
}

glad是用来管理opengl的函数指针的,所以启用opengl任何函数之前需要初始化glad
glViewport()是设置视口的,具体作用大概就是设置窗口的起始坐标,以及告诉opengl我们要渲染的窗口大小为800x600.
glfwSetFramebufferSizeCallback()目的是为了当我们拖动窗口,造成窗口大小变化时,能够及时改变渲染的窗口尺寸,里面传入了我们自己设置的函数,可以看到函数内;我们在实时更改视口。

void framebuffer_size_callback(GLFWwindow * window, int width, int height) //随用户调整窗口大小而变化视口大小
{
	glViewport(0, 0, width, height);
}

该准备的准备完了,可以开始渲染循环了。

	while (!glfwWindowShouldClose(window)) // 检查glfw是否被要求退出
	{
		glfwSwapBuffers(window); //交换前后缓冲
		glfwPollEvents(); //监视事件
	}

如果跟着前面打没有打错的话,至此一个完整的黑色背景窗口就能出现了。


来解释一下glfwSwapBuffers(),因为OPENGL是双缓冲,那么双缓冲的作用是什么呢?假如是单缓冲,由于屏幕上的像素绘制是由左到右,从上到下一个一个绘制的,如果缓冲量过大,那么可能会出现图像闪烁,但是如果是双缓冲,它是由前缓冲全部绘制好,然后再跟后缓冲互换,然后前后工作交替进行,这样子就解决了闪烁问题。所以也能理解这个函数为什么叫这么个名字了吧。

那么现在我们想给窗口背景改个色,该怎么弄呢,我们需要设置一个颜色缓冲。

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

glClearColor()是设置清空屏幕所使用的颜色,意思就是使用这个函数后,之后每次清屏,默认颜色都是我们这个函数所设置的颜色。然后glClear()就是清除颜色缓冲,也就是清屏,那么我们为什么每次循环都要glClear()呢,因为有些人可能认为,我都画上去了,为什么要清除屏幕,再绘制一次呢,因为在渲染循环里,你是一个在不断绘制图像的过程,如果不清屏,那么每次画的图像就会叠加,导致跟我们预想的绘制内容会出现很大差别,比如你只是要画一只鸭子,但你如果不使用glClear(),那可能就是成千上万只鸭子绘制在同一个地方,内容不断叠加。

最后我们再加入一个退出事件的函数:

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

可以看到一旦检测到我们按下ESC键,那么就会利用glfwSetWindowShouldClose()函数,传入true,使window窗口关闭,可以看到我们循环的条件是while (!glfwWindowShouldClose(window))

接下来是完整源码:

#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>
void framebuffer_size_callback(GLFWwindow * window, int width, int height);
void processInput(GLFWwindow * window);

int main()
{
	glfwInit(); //初始化glfw
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); //版本号
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); //代表glfw3.3

	//创建窗口
	GLFWwindow * window = glfwCreateWindow(800, 600, "LearnOpenGL", NULL, NULL);
	//判断是否创建成功
	if (window == NULL)
	{
		std::cout << "Failed to create GLFW window" << std::endl;
		glfwTerminate();
		return -1;
	}
    glfwMakeContextCurrent(window); //设置当前线程上下文 意思就是在切换下一个状态之前,进行的所有操作都是对 window这个对象进行的

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

    //开始渲染之前先告诉opengl渲染窗口的尺寸大小,以及坐标
	//注意,opengl坐标范围为-1到1 ,与屏幕坐标之间存 映射关系
	glViewport(0, 0, 800, 600); 
	//注册该函数,告诉opengl每次window调整大小时都调用该函数
	glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); 

    while (!glfwWindowShouldClose(window)) // 检查glfw是否被要求退出
	{
        processInput(window);

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

		glfwSwapBuffers(window); //交换前后缓冲
		glfwPollEvents(); //监视事件
	}

    //释放内存
	glfwTerminate(); 
	return 0;
}

void framebuffer_size_callback(GLFWwindow * window, int width, int height) //随用户调整窗口大小而变化视口大小
{
	glViewport(0, 0, width, height);
}

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

这样,一个基本完整的窗口就完成啦。

posted @ 2021-04-01 20:48  eveilcoo  阅读(471)  评论(0编辑  收藏  举报