在画出出色的效果以前,首先要作的就是建立一个 OpenGL 上下文和一个用于显式的窗口。
一些函数库已经提供了这样的功能,能够提供给开发者一个窗口和上下文来渲染。
比较流行的有 GLUT, SDL, SFML, GLFW, 此处咱们使用 GLFW.html
GLAD 一般和 GLFW 配合使用; GLEW 一般和 Freeglut 配合使用。c++
简介
GLFW 是一个专门针对 OpenGL 的 C 语言库,它提供了一些渲染物体所须要的最低限度的接口。它容许用户建立 OpenGL 上下文,定义窗口参数以及处理用户输入。git
官网下载
GLFW 须要从 GLFW 官网进行下载。
github
能够下载源文件而后配合 CMake 工具生成对应的二进制版本和头文件,具体的作法可参见连接:LearnOpenGL CN。
也能够直接下载了官网编译好的二进制版本,建议下载 32 位版本。缓存
百度云下载
https://pan.baidu.com/s/1CCtc0sy1WpkScIkP3GZD8A 提取码 9hjy函数
因为 OpenGL 驱动版本众多,它大多数函数的位置都没法在编译时肯定下来,须要在运行时查询。因此任务就落在了开发者身上,开发者须要在运行时获取函数地址并将其保存在一个函数指针中供之后使用。然而这个过程既复杂有繁琐,而 GLAD 库能够简化此过程。工具
官网下载
打开 GLAD 的在线服务,选择以下配置后进行生成,选择压缩文件 glad.zip 下载便可。spa
百度云下载
https://pan.baidu.com/s/1LlBCLbRNDSUNUKz4kzw_ow 提取码 54xk线程
将获得的压缩包进行解压。
为了方即可以将使用须要的文件放在指定的目录中。此处我选择将其放在文件夹 C:\Program Files\OpenGL 中。指针
移动后的文件结构以下:
OpenGL | include | glad |
GLFW | ||
KHR | ||
vc2019 | ... | |
src | glad.c |
新建一个源文件,要包含如下头文件
#include <glad/glad.h> #include <GLFW/glfw3.h>
建立 main
函数,实例化 GLFW 窗口
int main() { // 初始化 GLFW glfwInit(); // 配置 GLFW // 第一个参数是 enum 类型表示选项名称 // 第二个参数是 int 类型用于设置第一个参数的值 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); // 设置主版本号 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); // 设置次版本号 glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 使用核心模式 // 仅 Mac OS X 系统须要下面的语句 //glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); return 0; }
建立一个窗口对象
// glfwCreateWindow 函数用于建立窗口对象 glfwCreateWindow // 第1、2、三个参数分别是窗口的宽度、高度、窗口的标题 // 后两个参数暂时先忽略 // 返回一个 GLFWwindow 对象的指针 GLFWwindow* window = glfwCreateWindow(800, 600, "LearnOpenGL", NULL, NULL); if (window == NULL) { std::cout << "Failed to create GLFW window" << std::endl; glfwTerminate(); return -1; } // 窗口建立成功以后通知 GLFW 将窗口的上下文设置为当前线程的主上下文 glfwMakeContextCurrent(window);
加载系统储相关的 OpenGL 函数指针地址
GLAD 是用来管理 OpenGL 的函数指针的,因此任何调用 OpenGL 的函数以前都要初始化 GLAD。
// gladLoadGLLoader: GLAD 装载 GL 装载机 // glfwGetProcAddress: GLFW 根据编译的系统定义正确的函数 if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) { std::cout << "Failed to initialize GLAD" << std::endl; return -1; }
视口
在开始渲染以前必需要告诉 OpenGL 渲染矩形区域的尺寸大小,即视口(Viewport)。
视口的大小能够和窗口相同,也能够大于或者小于窗口的大小。
只有绘制在视口区域的图形才能显式,
// glViewport: 设置视口的锚点和大小 // 第1、二个参数控制视口左下角的位置,窗口左下角为(0, 0) // 第3、四个参数控制视口的大小(单位:像素) glViewport(0, 0, 800, 600);
当用户改变窗口的大小时,视口也应该被调整。
能够对窗口注册一个回调函数,它会在每次窗口大小被调整时被调用。
// 回调函数第一个参数是窗口对象指针 // 回调函数的后两个参数是窗口被改变以后的宽度和高度 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 是否被要求退出。glfwPollEvents
函数检测是否触发了什么事件、更新窗口状态,并调用对应的回调函数。glfwSwapBuffers
函数会交换颜色缓存,它在这一迭代中被用来绘制,而且做为输出显示在屏幕上。双缓冲(Double buffer)
应用程序使用单缓冲绘图时会可出现图像闪烁的问题,这是由于生成的图像不是一会儿被绘制出来的,而是一步步生成的,这致使渲染的结果不真实。
为了不这一问题,咱们采用双缓冲渲窗口应用程序。其中,前缓冲中保存着最终输出的图像;后换冲用于绘制全部的渲染指令。当全部的渲染指令完成以后,交换先后缓冲,图像就会当即显示出来。
正确释放/删除以前的分配的全部资源
// 正确释放/删除以前的分配的全部资源 glfwTerminate();
以获取一个按键输入为例:当按下 Escape 按键时关闭窗口。
// 建立一个 processInput 来处理输入 void processInput(GLFWwindow *window) { // glfwGetKey 用于检查按键 Escape 是否被按下(若是按下了返回 GLDFW_RELEASE) // glfwSetWindowShouldClose 用于 Escape 按下时将 WindowShouldClose 属性设置为 true if(glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) glfwSetWindowShouldClose(window, true); // 下一次循环就会关闭 GLFW } // 在循环渲染的每次迭代中调用 processInput while (!glfwWindowShouldClose(window)) { processInput(window); glfwSwapBuffers(window); glfwPollEvents(); }
咱们要把全部的渲染(Rendering)操做放到渲染循环中,从而保证渲染指令在每次渲染循环迭代的时候都能被执行。
// 渲染循环 while(!glfwWindowShouldClose(window)) { // 输入 processInput(window); // 渲染指令 ... // 检查并调用事件,交换缓冲 glfwPollEvents(); glfwSwapBuffers(window); }