这是对learnopengl的简单笔记。原教程网址:learnopengl。原教程同时涉及图形学的基本理论与opengl API,本文更多关注API,而简化甚至省略了背后的图形学原理性内容。
开始一个openGL程序十分繁琐,首先是怎么在GLFW下打开一个能显示东西、能交互的窗口。这里只涉及最简单的开关,但体现了某种框架。
创建窗口
在main函数的开头实例化一个窗口。
#include<GLFW/glfw3.h>
#include<glad/glad.h>
int main()
{
glfwInit(); //initial
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); //设置GLFW主版本号
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); //设置GLFW次版本号
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); //只使用openGL的子集,在核模式下运行,不考虑向前兼容性。
#ifdef __APPLE__ //Mac加上下面的.
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif
}
接下来创建窗口对象,glCreateWindow(),得到这个窗口的 指针。
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能够找到这些函数的地址(函数指针)。调用任何openGL函数前,应初始化GLAD。
gladLoadGLLoder((GLADloadproc)glfwGetProcAddress)
if(!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
其中,GLADloadproc是一个定义在glad.h中的函数指针类型:
typedef void* (*GLADloadproc)(const char* procname);
上面的 (GLADloadproc)glfwGetProcAddress 是一个强制类型转换。
窗口的调整
用户往往对窗口进行操作与调整,比如会拖动窗口框调整大小等,需要回调函数(callback) 可以实现这个功能。
比如,调整视口大小 (glViewPort(0,0,width,height)。
void framebuffer_size_callback(GLFWwindow *window, int width, int height)
{
//自定义的回调函数.
glViewPort(0,0,width,height);//参数意义依次是左下角横纵坐标、窗口宽、高。
}
//设置回调函数
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
设置回调函数 glfwSet…callback(GLFWwindow*,…yourfuncptr) 的意义是将自定义的回调函数(一般是将调整函数包装出一个有符合要求的返回值、参数类型的函数,比如这里把 glViewPort 包装成符合要求的 framebuffer_size_callback 函数,以窗口指针为第一个参数)。作用是监测用户或OS对窗口的操作,一旦有改变,就把新的width,height传入所设置的自定义回调函数中并执行。
类比 Verilog 的 monitor 系统命令,或者一种异常处理函数。
有很多需要用回调功能的窗口函数,比如窗口大小glfwwindowsize等,窗口和视口不一样,后者是显示所绘图像的地方,前者就是一个窗口。
渲染循环(帧更新)
while中,,渲染语句块…
判断是否应关闭窗口:glfwWindowShouldClose(GLFWwindow*)。
信号处理(异常,如鼠标输入、键盘输入等):glfwPollEvents()。检测是否有信号并调用相应的回调函数(自己写的异常处理、信号处理函数)。处理所有事件队列!
交换颜色缓冲并把新缓冲打在屏幕上:glfwSwapBuffers(GLFWwindow*)。相当于刷新。
自定义输入信号处理函数,比如让 ESC 控制窗口关闭。用 glfwGetKey() 检查当前窗口(线程)的待处理信号队列(poll向量)里是否有相应的信号。
void processInput(GLFWwindow *window)
{
if(glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
}
在每个循环的开头调用这个函数以检查。
总的窗口简单模板代码
可以在 learnopengl 中找到。
#include<stdio.h>
#include <glad/glad.h>
#include <GLFW/glfw3.h>
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow *window);
// settings
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;
int main()
{
// glfw: initialize and configure
// ------------------------------
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#ifdef __APPLE__
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif
// glfw window creation
// --------------------
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
if (window == NULL)
{
printf("Failed to create GLFW window\n");
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
// glad: load all OpenGL function pointers
// ---------------------------------------
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
printf("Failed to initialize GLAD\n");
return -1;
}
// render loop
// -----------
while (!glfwWindowShouldClose(window))
{
// input
// -----
processInput(window);
// render
// ------
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
// -------------------------------------------------------------------------------
glfwSwapBuffers(window);
glfwPollEvents();
}
// glfw: terminate, clearing all previously allocated GLFW resources.
// ------------------------------------------------------------------
glfwTerminate();
return 0;
}
// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly
// ---------------------------------------------------------------------------------------------------------
void processInput(GLFWwindow *window)
{
if(glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, 1);
}
// glfw: whenever the window size changed (by OS or user resize) this callback function executes
// ---------------------------------------------------------------------------------------------
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
// make sure the viewport matches the new window dimensions; note that width and
// height will be significantly larger than specified on retina displays.
glViewport(0, 0, width, height);
}