从今天开始我们开始学习OpenGL,从0开始,当然是有C++基础的前提
首先包含glad和GLFW的头文件
#include
#include
#include
初始化 GLFW
在 main 函数中,我们首先使用 glfwInit 初始化 GLFW,然后我们可以使用 glfwWindowHint 配置 GLFW,这个配置的选项和含义可以在GLFW:窗口指南里面找到非常详细的解释,这个还是当成工具查阅就行,我们真正要操作的地方不在窗口初始化这里
int main() {
glfwInit(); // 初始化GLFW
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); // 指定创建的内容必须兼容的客户端 API 版本
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); // 指定创建的内容必须兼容的客户端 API 版本
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 指定要为其创建内容的 OpenGL 配置文件
//glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // 指定 OpenGL 内容是否应向前兼容
return 0;
}
创建窗口对象
接下来,我们需要创建一个窗口对象,glfwCreateWindow 函数前两个参数是窗口的宽度和高度,第三个参数是窗口的名称,后面两个参数的作用可以在GLFW:窗口指南找到
glfwMakeContextCurrent(window)告诉 GLFW 将窗口的内容作为当前线程上的主要内容
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);
初始化 GLAD
GLAD 管理 OpenGL 的函数指针,需要在调用任何 OpenGL 函数之前初始化 GLAD,我们向 GLAD 传递函数以加载特定于操作系统的 OpenGL 函数指针的地址,GLFW 为我们提供了glfwGetProcAddress,它根据我们编译的操作系统定义了正确的函数
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
渲染窗口调整
用glViewport设置 OpenGL 渲染窗口的大小,前两个参数设置窗口左下角的位置,第三个和第四个参数以像素为单位设置渲染窗口的宽度和高度,如果视口尺寸设置为小于 GLFW 尺寸的值;然后,所有 OpenGL 渲染都将显示在一个较小的窗口中
glViewport(0, 0, 800, 600);
当用户调整窗口大小时,视口也应该进行相应的调整,注册一个函数将 GLFWwindow 作为其第一个参数,并将两个表示新窗口尺寸的整数作为其第一个参数
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
glViewport(0, 0, width, height);
}
告诉 GLFW,通过注册它来在每次调整窗口大小时调用此函数
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
渲染循环
创建一个 while 循环,称之为渲染循环,它一直运行,直到我们告诉 GLFW 停止,
while(!glfwWindowShouldClose(window))
{
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwWindowShouldClose 函数在每次循环迭代开始时检查是否已指示 GLFW 关闭。如果是这样,函数将返回并且渲染循环停止运行,之后我们可以关闭应用程序
glfwSwapBuffers 将交换在此渲染迭代期间用于渲染的颜色缓冲区(一个大型 2D 缓冲区,其中包含 GLFW 窗口中每个像素的颜色值),并将其显示为输出到屏幕
双缓冲区
当应用程序在单个缓冲区中绘制时,生成的图像可能会显示闪烁问题。这是因为生成的输出图像不是瞬间绘制的,而是逐像素绘制的,通常从左到右和从上到下绘制。
由于此图像在呈现时不会立即显示给用户,因此结果可能包含伪影。为了规避这些问题,窗口化应用程序应用双缓冲区进行渲染。
前端缓冲区包含屏幕上显示的最终输出图像,而所有渲染命令都绘制到后端缓冲区。
一旦所有渲染命令都完成,我们就会将后面的缓冲区交换到前面的缓冲区,这样就可以显示图像,而不必仍然渲染到其中,从而删除了所有上述伪影。
glfwPollEvents 函数检查是否触发了任何事件(如键盘输入或鼠标移动事件),更新窗口状态,并调用相应的函数(我们可以通过回调方法注册)
一旦我们退出渲染循环,我们希望正确地清理/删除所有已分配的 GLFW 资源。我们可以通过在主函数末尾调用的 glfwTerminate 函数来做到这一点
glfwTerminate();
return 0;
编译运行
如果一切顺利,那么运行程序我们会看到一个黑色窗口
如果不行,请看全部源代码分析
#include
#include
#include
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
glViewport(0, 0, width, height);
}
int main() {
glfwInit(); // 初始化窗口
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); // 指定创建的内容必须兼容的客户端 API 版本
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); // 指定创建的内容必须兼容的客户端 API 版本
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 指定要为其创建内容的 OpenGL 配置文件
//glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // 指定 OpenGL 上下文是否应向前兼容
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);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); // 注册窗口调整调用函数
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
while(!glfwWindowShouldClose(window))
{
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwTerminate();
return 0;
}
如果想换个颜色的话,可以使用 glClearColor 指定要清除屏幕的颜色,每当我们调用 glClear 并清除颜色缓冲区时,整个颜色缓冲区都将填充 glClearColor 配置的颜色,其中颜色的选项是rgb和透明度四个通道参数
glClearColor(0.0f,0.5f,0.5f,1.0f);
while(!glfwWindowShouldClose(window))
{
glClear(GL_COLOR_BUFFER_BIT);
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwTerminate();