开始之前
配置好glfw和glew/glad之后,仍需要一些初始环境,首先需要在main中实例化GLFW的窗口。
int main()
{
glfwInit(); //初始化
//配置GLFW
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
//glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
return 0;
}
这是初始化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/GLEW,之后就可以使用OpenGL的函数
//GLEW
if (glewInit() != GLEW_OK)
{
std::cout << "Error" << std::endl;
}
//GLAD
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
视口
Viewport,定义渲染输出的区域,视口将窗口坐标映射到裁剪空间坐标,从而决定了渲染内容在窗口中的显示位置和大小,在开始渲染之前需要指定,可以将视口的维度设置的小于GLFW的维度,这样实际渲染的内容将在更小的窗口内,这样就可以将一些其他元素显示在OpenGL视口之外,默认则是整个GLFW。
glViewport(0, 0, 800, 600);
视口需要会随着窗口大小改变,所以可以对窗口注册一个回调函数来处理:
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
在函数中定义
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
int viewportwidth = width - 100;
glViewport(0, 0, viewportwidth, height);
}
接下来就可以注册这个回调函数
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
当窗口被第一次显示的时候framebuffer_size_callback也会被调用
设置视口是有必要的,如果没有设置视口,那么拖动窗口的效果将是
设置视口之后:
效果很明显,视口定义了窗口坐标到裁剪空间坐标的映射关系。如果不设置视口,当窗口大小改变时,OpenGL仍然使用初始的视口设置,这会导致渲染内容无法正确适应新的窗口尺寸,从而出现内容缺失或变形的情况。
循环
之前的都只是设置窗口,设置显示,设置GLFW,接下来为渲染做准备工作。图形渲染需要一个RenderLoop
/* Loop until the user closes the window */
while (!glfwWindowShouldClose(window))
{
/* Render here */
glClear(GL_COLOR_BUFFER_BIT);//每个渲染迭代钱总是清屏
/* Swap front and back buffers */
glfwSwapBuffers(window);//交换颜色缓冲
/* Poll for and process events */
glfwPollEvents();//检查有没有触发事件/更新窗口状态/调用回调
}
事后
渲染循环结束后我们需要正确释放/删除之前的分配的所有资源。我们可以在main函数的最后调用glfwTerminate函数来完成。
glfwTerminate();
return 0;
输入
希望能够在GLFW中实现一些输入控制,这可以通过使用GLFW的几个输入函数来完成。我们将会使用GLFW的glfwGetKey函数,它需要一个窗口以及一个按键作为输入。这个函数将会返回这个按键是否正在被按下。我们将创建一个processInput函数来让所有的输入代码保持整洁。
比如,想要在按下esc之后退出
void processInput(GLFWwindow* window)
{
if (glfwGetKey(window,GLFW_KEY_ESCAPE) == GLFW_PRESS)
{
glfwSetWindowShouldClose(window, true); //设置关闭状态为true
}
}
之后只需要在循环内调用该函数即可。
渲染
将所有的渲染操作放在渲染循环中
// 渲染循环
while(!glfwWindowShouldClose(window))
{
glClear(GL_COLOR_BUFFER_BIT);
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
// 输入
processInput(window);
// 渲染指令
...
// 检查并调用事件,交换缓冲
glfwPollEvents();
glfwSwapBuffers(window);
}