在第一课中我们就已经说过 OpenGL 并不会直接处理 Windows 相关操作,这一部分功能都是由其他 API (如 GLX、WGL 等) 负责。为了简单起见我们使用了 GLUT 处理 Windows API 的调用,由于 GLUT 是一个跨平台的库,所以我们的程序也可以移植到不同的操作系统中。到目前为止我们的程序中都只使用了 GLUT 库,而现在我们将会介绍另外一个比较流行的库,这个库的功能和 GLUT 相似,它叫做 GLFW,并且托管在 www.glfw.org 上。这两个库之间的区别之一就在于 GLFW 更加先进而且有活力,但是 GLUT 则显得比较老旧,而且它的开发维护现在也基本上停滞了。 GLFW 有很多特性你可以在它的主页上进一步了解。
由于这一课中没有什么数学背景,所有我们可以直接去参看代码即可。这里我做出的改变是对 glut_backend.h 和 glut_backend.cpp 中对 Windows 接口和键盘按键等事件的封装进行了抽象,抽象出了一个 backend API,通过这个 API 我们可以方便的在 GLUT 接口和 GLFW 接口之间转换,并且为我们之后的学习提供了很大的灵活性。
(ogldev_glfw_backend.cpp:24) |
#define GLFW_DLL |
#include |
(ogldev_glfw_backend.cpp:168) |
void GLFWBackendInit(int argc, char** argv, bool WithDepth, bool WithStencil) |
{ |
sWithDepth = WithDepth; |
sWithStencil = WithStencil; |
if (glfwInit() != 1) { |
OGLDEV_ERROR("Error initializing GLFW"); |
exit(1); |
} |
int Major, Minor, Rev; |
glfwGetVersion(&Major, &Minor, &Rev); |
printf("GLFW %d.%d.%d initialized\n", Major, Minor, Rev); |
glfwSetErrorCallback(GLFWErrorCallback); |
} |
(ogldev_glfw_backend.cpp:195) |
bool GLFWBackendCreateWindow(uint Width, uint Height, bool isFullScreen, const char* pTitle) |
{ |
GLFWmonitor* pMonitor = isFullScreen ? glfwGetPrimaryMonitor() : NULL; |
s_pWindow = glfwCreateWindow(Width, Height, pTitle, pMonitor, NULL); |
if (!s_pWindow) { |
OGLDEV_ERROR("error creating window"); |
exit(1); |
} |
glfwMakeContextCurrent(s_pWindow); |
// Must be done after glfw is initialized! |
glewExperimental = GL_TRUE; |
GLenum res = glewInit(); |
if (res != GLEW_OK) { |
OGLDEV_ERROR((const char*)glewGetErrorString(res)); |
exit(1); |
} |
return (s_pWindow != NULL); |
} |
在我们使用 GL 命令之前我们需要将创建的窗口设置为当前窗口,我们使用 glfwMakeContextCurrent 函数来完成这个操作,最后我们初始化 GLFW。
(ogldev_glfw_backend.cpp:238) |
while (!glfwWindowShouldClose(s_pWindow)) { |
// OpenGL API calls go here... |
glfwSwapBuffers(s_pWindow); |
glfwPollEvents(); |
} |
(ogldev_glfw_backend.cpp:122) |
static void KeyCallback(GLFWwindow* pWindow, int key, int scancode, int action, int mods) |
{ |
} |
static void CursorPosCallback(GLFWwindow* pWindow, double x, double y) |
{ |
} |
static void MouseCallback(GLFWwindow* pWindow, int Button, int Action, int Mode) |
{ |
} |
static void InitCallbacks() |
{ |
glfwSetKeyCallback(s_pWindow, KeyCallback); |
glfwSetCursorPosCallback(s_pWindow, CursorPosCallback); |
glfwSetMouseButtonCallback(s_pWindow, MouseCallback); |
} |
(ogldev_glfw_backend.cpp:) |
void GLFWBackendTerminate() |
{ |
glfwDestroyWindow(s_pWindow); |
glfwTerminate(); |
} |
(ogldev_backend.h) |
enum OGLDEV_BACKEND_TYPE { |
OGLDEV_BACKEND_TYPE_GLUT, |
OGLDEV_BACKEND_TYPE_GLFW |
}; |
void OgldevBackendInit(OGLDEV_BACKEND_TYPE BackendType, int argc, char** argv, bool WithDepth, bool WithStencil); |
void OgldevBackendTerminate(); |
bool OgldevBackendCreateWindow(uint Width, uint Height, bool isFullScreen, const char* pTitle); |
void OgldevBackendRun(ICallbacks* pCallbacks); |
void OgldevBackendLeaveMainLoop(); |
void OgldevBackendSwapBuffers(); |
由于这一课中并没有什么新的东西呈现,所以我们使用了一个 Sponza 模型,这个模型在测试全局光照时经常用到。