1.编译GLFW生成LIB库文件
由于在Windows平台下初始化OpenGL非常麻烦,不仅需要创建OpenGL上下文,而且更麻烦的是调用OpenGL的GPU驱动的函数(具体多麻烦查看我的博文)。所以推荐使用第三方已经写好的库GLFW,类似的库还有GLUT。
GLFW是跨平台的抽象层,所以同一份代码在Windows和Linux下都可以运行。
GLFW提供了创建窗口,创建OpenGL上下文,管理键盘和鼠标的事件,甚至还提供多线程管理等跨平台的功能。功能越来越多了。
在GLFW官网上,找到他们的源码页,并下载到本地。
进入到glfw目录下
创建build文件夹,然后使用cmake构建出glfw项目文件。
cd glfw
mkdir build
cd build
cmake
用VS打开GLFW.sln
执行项目,执行完毕后,观察src/Release文件夹下多了我们需要的静态链接库文件glfw3.lib
2.使用GLFW库
1.以动态库方式使用
以后使用GLFW库时,在项目中添加包含的头目录和库目录
打开VS开发工具选择下面菜单:
头目录:
"Project>Properties>Configuration Properties>VC++ Directories>General> Include Directories”
添加
"D:\libs\glfw\include"
(上面路劲你根据自己情况填写)
2.以静态库方式使用
在这里我建议直接把静态链接库文件拷贝到项目下。
然后在项目代码中将库文件包含进去。
#pragma comment (lib, “glfw3.lib”)
这样生成的代码就包含静态库的内容了,移植时减少了bug的风险。
代码样例:
#include <GLFW/glfw3.h>
#include <gl/GL.h>
#include <iostream>
#pragma comment (lib, "opengl32.lib")
#pragma comment (lib, "glfw3.lib")
void error_callback(int code, const char* description);
int main(void)
{
GLFWwindow* window;
/* Handle GLFW Error */
glfwSetErrorCallback(error_callback);
/* Initialize the library */
if (!glfwInit())
return -1;
/* Create a windowed mode window and its OpenGL context */
window = glfwCreateWindow(640, 480, "Hello World", NULL, NULL);
if (!window)
{
glfwTerminate();
return -1;
}
/* Make the window's context current */
glfwMakeContextCurrent(window);
/* 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();
}
glfwTerminate();
return 0;
}
void error_callback(int code, const char* description)
{
std::cout << code << ":" << description << std::endl;
}
3. GLFW的基本用法
GLFW初始化和终止
在使用GLFW大部分函数之前需要对GLFW库进行初始化,初始化的工作内容包括检查平台包含的特定功能。GLFW库初始化通过glfwInit进行。
if(!glfwInit())
{
// handle initialization failure
}
如果初始化成功则会返回GLFW_TRUE、否则返回GLFW_FALSE。
在初始化之前,GLFW函数中只有下面几个函数可以使用,调用其他函数将会报GLFW_NOT_INITALIZED错误。
glfwGetVersion
glfwGetVersionString
glfwGetError
glfwSetErrorCallback
glfwInitHint
在应用程序退出之前应先释放GLFW库占用的系统资源。这可以通过glfwTerminate实现。2.2 GLFW错误处理
有些GLFW函数出错会返回对你解决错误没什么用的错误值,更有甚者有些函数还没返回值。所以处理GLFW的错误就需要独立的通道。在GLFW出现错误时会抛出错误代码,我们可以通过glfwGetError捕获。如下所示:
const char* description;
int code = glfwGetError(&description);
if(code!=GLFW_NO_ERROR)
{
// display error message...
// handle error ...
}
字符指针description会指向以UTF-8码形式存储的错误信息内存处。
然而更推荐的做法是设置错误回调函数glfwSetErrorCallback。这样每次glfw出现错误都会调用回调函数。
glfwSetErrorCallback(error_callback);
void error_callback(int code, const char* description)
{
// display error...
}
创建窗口和OpenGL上下文
GLFW库通过glfwCreateWindow函数进行创建OpenGL上下文和创建窗口。创建之前若想定制自已想要的窗口和上下文而不是系统默认的,可通过window hint设置属性。你可以创建多个窗口和上下文,但是,仅被make current的窗口和上下文是起作用的。要想查询已经创建好的窗口和上下文属性,可通过glfwGetWindowAttrib、glfwGetWindowSize、glfwGetFramebufferSize查询。
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
GLFWwindow* window = glfwCreateWindow(640, 480, "My Title", NULL, NULL);
if (!window)
{
// Window or context creation failed
}
上面代码设置了OpenGL上下文的最低版本,如果机器不满足最低版本的限制,将会创建失败。
如果不在需要一个窗口了,摧毁它。
glfwDestroyWindow(window);
Make Context Current
使用OpenGL的API必须有而且只能有一个OpenGL上下文被设置成Current。
OpenGL上下文就是一个个状态机,显卡驱动程序只对Current上下文起作用。
GLFW设置current的语句和Windows的很像。
glfwMakeContextCurrent(window);
注:如果你是通过glad或gl3w来使用OpenGL,你就必需在使用它们之前拥有一个current context。
检查窗口关闭的标识
在GLFW中,每个窗口都有一个标识,用来标记窗口是否应该被关闭。当用户关闭了一个窗口,该标记就被设置成了1,所以窗口并没有被关闭,你需要时刻检查这个标记。
检查的方式是通过函数glfwWindowShouldClose。
返回的就是窗口关闭的标识值。
while (!glfwWindowShouldClose(window))
{
// Keep running
}
你也可以设置窗口的回调函数,每次关闭窗口时都会触发该函数。
如下所示:
glfwSetWindowCloseCallback(window, window_close_callback);
...
void window_close_callback(GLFWwindow* window)
{
glfwSetWindowShouldClose(window, 0);
}
键盘事件响应
GLFW提供了设置键盘响应的回调函数glfwSetKeyCallback,如下所示:
glfwSetKeyCallback(window, key_callback);
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
glfwSetWindowShouldClose(window, GLFW_TRUE);
}
交换缓冲区与处理事件
GLFW创建的窗口有两个缓冲区,前缓冲区和后缓冲区,渲染是在后缓冲区上进行的,一旦缓冲区满了,就需要交换前后缓冲区,通过glfwSwapBuffers(window)。
事件发生时,并不是立即得到处理的,而是暂时挂起。这些挂起的事件需要定期处理,通常在缓冲区交换的每一帧后完成。
如下所示:
/* Swap front and back buffers */
glfwSwapBuffers(window);
/* Poll for and process events */
glfwPollEvents();
参考资料
https://www.glfw.org/docs/latest/quick.html#quick_steps