前言
本节笔记对应的内容 你好,窗口。
在之前的入门01中我们配置好了环境,下面开始正式的绘制一个窗口。
目标①:打开一个黑窗口
首先我们创建一个main函数,用来实例化glfw的窗口:
int main()
{
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
return 0;
}
- glfwInit函数用来初始化GLFW,
- glfwWindowHint函数来配置GLFW,
- MAJOR与MINOR,分别用来指定OpenGL的主版本号和次版本号,
然后告诉GLFW我们使用的是核心模式(Core-profile)。
接下来创建窗口对象:
GLFWwindow* window = glfwCreateWindow(800,600,"My OpenGL Game",NULL,NULL);
if(window == NULL)
{
printf("Open window failed.");
glfwTerminate();
return - 1;
}
glfwMakeContextCurrent(window);
800与600分别是窗口的宽与高,第三个参数为标题。
- 关于Context
OpenGL自身是一个巨大的状态机(State Machine):有一系列的变量来描述OpenGL此刻应当如何运行。
OpenGL的状态通常被称为OpenGL上下文(Context)。
通常我们使用如下途径去更改OpenGL状态:设置选项,操作缓冲。最后,我们使用当前OpenGL上下文来渲染。
创建完窗口后和错误提醒后,就可以通知GLFW,将我们窗口的上下文设置为当前线程的主上下文了。
接下来初始化GLEW,打开实验性质的功能以及错误提醒:
glewExperimental = true;
if (glewInit() != GLEW_OK)
{
printf("Init GLEW failed.");
glfwTerminate();
return -1;
}
在开始渲染之前,需要告诉OpenGL渲染窗口的尺寸大小,即视口(Viewport)。可以通过调用glViewport函数来设置窗口的维度(Dimension):
glViewport(0, 0, 800, 600);
- 前两个参数X,Y————以像素为单位,指定了视口的左下角(在第一象限内,以(0,0)为原点的)位置。
- 后两个参数width,height————表示这个视口矩形的宽度和高度,根据窗口的实时变化重绘窗口。
在程序中添加一个while循环,渲染循环(Render Loop),它能在我们让GLFW退出前一直保持运行。下面实现了一个简单的渲染循环:
while (!glfwWindowShouldClose(window))
{
glfwSwapBuffers(window);
glfwPollEvents();
}
- glfwWindowShouldClose函数在我们每次循环的开始前检查一次GLFW是否被要求退出,如果是的话该函数返回true然后渲染循环便结束了,之后为我们就可以关闭应用程序了。
- glfwPollEvents函数检查有没有触发什么事件(比如键盘输入、鼠标移动等)、更新窗口状态,并调用对应的回调函数(可以通过回调方法手动设置)。
- glfwSwapBuffers函数会交换颜色缓冲(它是一个储存着GLFW窗口每一个像素颜色值的大缓冲),它在这一迭代中被用来绘制,并且将会作为输出显示在屏幕上。
- 双缓冲(Double Buffer):
应用程序使用单缓冲绘图时可能会存在图像闪烁的问题。
这是因为生成的图像不是一下子被绘制出来的,而是按照从左到右,由上而下逐像素地绘制而成的。
最终图像不是在瞬间显示给用户,而是通过一步一步生成的,这会导致渲染的结果很不真实。
为了规避这些问题,应用双缓冲渲染窗口应用程序。
前缓冲保存着最终输出的图像,它会在屏幕上显示;而所有的的渲染指令都会在后缓冲上绘制。
当所有的渲染指令执行完毕后,我们交换(Swap)前缓冲和后缓冲,这样图像就立即呈显出来,之前提到的不真实感就消除了。
正确释放/删除之前的分配的所有资源
glfwTerminate();
return 0;
实现效果为一个黑窗口:
以下是这一步后main.cpp的完整代码:
#include <iostream>
#define GLEW_STATIC
#include <GL/glew.h>
#include <GLFW/glfw3.h>
int main() {
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR,3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
//Open GLFW Window
GLFWwindow* window = glfwCreateWindow(800,600,"My OpenGL Game",NULL,NULL);
if(window == NULL)
{
printf("Open window failed.");
glfwTerminate();
return - 1;
}
glfwMakeContextCurrent(window);
//Init GLEW
glewExperimental = true;
if (glewInit() != GLEW_OK)
{
printf("Init GLEW failed.");
glfwTerminate();
return -1;
}
glViewport(0, 0, 800, 600);
while (!glfwWindowShouldClose(window))
{
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwTerminate();
return 0;
}
目标②:输入检测与背景颜色
- 输入检测
在GLFW中实现一些输入控制,可以通过使用GLFW的几个输入函数来完成。
比如GLFW的glfwGetKey函数,它需要一个窗口以及一个按键作为输入。这个函数将会返回这个按键是否正在被按下。
我们将创建一个processInput函数来实现这个功能。
void processInput(GLFWwindow* window){
if (glfwGetKey(window, GLFW_KEY_ESCAPE )== GLFW_PRESS)
{
glfwSetWindowShouldClose(window, true);
}
}
- 在渲染循环中调用输入检测
while (!glfwWindowShouldClose(window))
{
processInput(window);
glfwSwapBuffers(window);
glfwPollEvents();
}
- 渲染
渲染指令的结构应该如下所示
// 渲染循环
while(!glfwWindowShouldClose(window))
{
// 输入
processInput(window);
// 渲染指令
...
// 检查并调用事件,交换缓冲
glfwSwapBuffers(window);
glfwPollEvents();
}
- 背景颜色
我们使用一个自定义的颜色清空屏幕。
我们在每个新的渲染迭代开始的时候总是希望能清屏,否则上一次迭代的渲染结果仍会被看见。
可以通过调用glClear函数来清空屏幕的颜色缓冲,它接受一个缓冲位(Buffer Bit)来指定要清空的缓冲,可能的缓冲位有GL_COLOR_BUFFER_BIT,GL_DEPTH_BUFFER_BIT和GL_STENCIL_BUFFER_BIT。
由于现在我们只是设定背景颜色,所以我们只清空颜色缓冲。
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
实现的效果应该是一个有颜色的窗口,同时可以按esc退出
以下是这一步后main.cpp的完整代码:
#include <iostream>
#define GLEW_STATIC
#include <GL/glew.h>
#include <GLFW/glfw3.h>
void processInput(GLFWwindow* window){
if (glfwGetKey(window, GLFW_KEY_ESCAPE )== GLFW_PRESS)
{
glfwSetWindowShouldClose(window, true);
}
}
int main() {
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR,3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
//Open GLFW Window
GLFWwindow* window = glfwCreateWindow(800,600,"My OpenGL Game",NULL,NULL);
if(window == NULL)
{
printf("Open window failed.");
glfwTerminate();
return - 1;
}
glfwMakeContextCurrent(window);
//Init GLEW
glewExperimental = true;
if (glewInit() != GLEW_OK)
{
printf("Init GLEW failed.");
glfwTerminate();
return -1;
}
glViewport(0, 0, 800, 600);
while (!glfwWindowShouldClose(window))
{
processInput(window);
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwTerminate();
return 0;
}