本文主要说明OpenGL的编程模式:
1. OpenGK环境与上下文初始化;
2. 错误处理;
3. 版本管理;
- 说明
- GLFW与GLEW无关,可以独立调用;主要负责UI部分。
一. GLFW的官方文档
1. 官方参考
https://www.glfw.org/docs/latest/modules.html
2. 在线教程
https://www.glfw.org/docs/latest/
-
- 教程分成6个主题来说明:
- 初始化:其实就是编程模式;
- Window:创建窗体组件;
- Context:OpenGL与OpenGL ES的上下文环境;
- Vulkan:一个跨平台的2D和3D绘图应用程序接口(API);
- Monitor:监视器与视频工作模式;
- Input:交互事件处理:键盘与鼠标交互输入;
二. GLFW编程模式
1. 初始化与释放终止
1.1. 函数说明
-
GLFWAPI void glfwGetVersion(int* major, int* minor, int* rev);
- 函数说明:
- 获取运行时GLFW的版本;
- 函数参数:
- 返回主版本号,副版本号,修订号
- 函数返回值:
- 无
- 说明:
- 该函数的调用,无需调用glfwInit函数。
- 函数说明:
-
GLFWAPI const char* glfwGetVersionString(void);
- 与上面函数一样,只是返回字符串格式的版本号;
-
GLFWAPI int glfwGetError(const char** description);
- 函数说明:
- 用来获取上次执行函数发生的错误,某些函数没有返回值,从而无法返回错误状态,使用该函数就非常方便。
- 函数参数:
- 返回错误的字符串描述;需要双指针,一个存放地址的地址,这意味着错误描述的字符串空间是函数分配的。
- 没有错误发生,返回NULL。
- 函数返回值:
- 返回上次函数执行产生的错误码;
- 说明:
- 返回GLFW_NO_ERROR表示没有错误发生。GLFW_NO_ERROR的定义如下:
#define GLFW_NO_ERROR 0
- 返回GLFW_NO_ERROR表示没有错误发生。GLFW_NO_ERROR的定义如下:
- 函数说明:
- GLFWAPI GLFWerrorfun glfwSetErrorCallback(GLFWerrorfun cbfun);
- 函数说明:
- 用来设置函数错误发生时候的回调函数。
- 函数参数:
- 回调函数,该函数原型定义如下(两个参数,用来传递错误码与错误描述):
typedef void (* GLFWerrorfun)(int,const char*);
- 回调函数,该函数原型定义如下(两个参数,用来传递错误码与错误描述):
- 函数返回值:
- 返回上次设置的错误回调函数,上次没有设置则返回NULL。
- 函数说明:
-
GLFWAPI void glfwInitHint(int hint, int value);
- 函数说明:
- 在glfwinit之前调用用来设置初始化提示,提示设置会影响初始化行为,并最终影响库的行为,直到环境终止。
- 函数参数:
- int hint:设置提示的类型;
GLFW_JOYSTICK_HAT_BUTTONS(缺省值:GLFW_TRUE 支持值:GLFW_TRUE 或者 GLFW_FALSE)
GLFW_COCOA_CHDIR_RESOURCES(缺省值:GLFW_TRUE 支持值:GLFW_TRUE 或者 GLFW_FALSE)
GLFW_COCOA_MENUBAR(缺省值:GLFW_TRUE 支持值:GLFW_TRUE 或者 GLFW_FALSE)
- int value:设置提示的值;支持的值:
- GLFW_TRUE
- GLFW_FALSE
3. 函数返回值:
- 无
4. 说明:
- 某些平台有自己特殊的提示设置。
- 可能产生的错误有:GLFW_INVALID_ENUM 与 GLFW_INVALID_VALUE;
- int hint:设置提示的类型;
- 函数说明:
-
GLFWAPI int glfwInit(void);
- 函数说明:
此函数初始化glfw库。在大多数glfw函数可以使用之前,必须初始化glfw,并且在应用程序终止之前,应该终止glfw,以便释放初始化期间或之后分配的任何资源。
如果此函数失败,则在返回之前调用glfwterminate函数。
如果成功,则应在应用程序退出之前调用glfwterminate函数。
如果还函数已经成功初始化,则再此调用将立即返回GLFW_TRUE。
此函数将应用程序的当前目录更改为应用程序包的“Contents/Resources”子目录(如果存在)。这可以通过GLFW_COCOA_CHDIR_RESOURCES的init hint禁用。
- 函数参数
- 无
- 函数返回值:
- 成功返回GLFW_TRUE
- 失败返回GLFW_FALSE
- 说明:
- GLFW_TRUE与GLFW_FALSE的定义:
#define GLFW_TRUE 1
#define GLFW_FALSE 0
- 可能得错误:GLFW_PLATFORM_ERROR,使用函数error_handling处理。
- GLFW_TRUE与GLFW_FALSE的定义:
- 函数说明:
- GLFWAPI void glfwTerminate(void);
- 函数说明:
- 此函数销毁所有剩余的窗口和光标,恢复任何修改过的gamma渐变并释放任何其他分配的资源。 调用此函数后,必须再次成功调用glfwInit,才能使用大多数glfw函数。
- 如果glfw已成功初始化,则应在应用程序退出之前调用此函数。
- 如果初始化失败,则无需调用此函数,因为它在返回failure之前由glfwinit调用。
- 该函数不要在回调函数中调用;
- 该函数只能在主线程中调用;
- 函数参数:
- 无
- 函数返回值:
- 无
- 函数说明:
1.2. 关于GLFWAPI
-
GLFWAPI是一个使用#define定义的宏,用来说明函数来自DLL库。
__declspec(dllexport)
-
说明:
dllexport是在这些类、函数以及数据的申明的时候使用。用他表明这些东西可以被外部函数使用,即(dllexport)是把 DLL中的相关代码(类,函数,数据)暴露出来为其他应用程序使用。
使用了(dllexport)关键字,相当于声明了紧接在(dllexport)关键字后面的相关内容是可以为其他程序使用的。
dllimport是在外部程序需要使用DLL内相关内容时使用的关键字。当一个外部程序要使用DLL 内部代码(类,函数,全局变量)时,只需要在程序内部使用(dllimport)关键字声明需要使用的代码就可以了,即(dllimport)关键字是在外部程序需要使用DLL内部相关内容的时候才使用。(dllimport)作用是把DLL中的相关代码插入到应用程序中。
_declspec(dllexport)与_declspec(dllimport)是相互呼应,只有在DLL内部用dllexport作了声明,才能在外部函数中用dllimport导入相关代码。
/* GLFWAPI is used to declare public API functions for export
* from the DLL / shared library / dynamic library.
*/
#if defined(_WIN32) && defined(_GLFW_BUILD_DLL)
/* We are building GLFW as a Win32 DLL */
#define GLFWAPI __declspec(dllexport)
#elif defined(_WIN32) && defined(GLFW_DLL)
/* We are calling GLFW as a Win32 DLL */
#define GLFWAPI __declspec(dllimport)
#elif defined(__GNUC__) && defined(_GLFW_BUILD_DLL)
/* We are building GLFW as a shared / dynamic library */
#define GLFWAPI __attribute__((visibility("default")))
#else
/* We are building or calling GLFW as a static library */
#define GLFWAPI
#endif
1.3. 运行初始化与释放
- 第一个运行的函数必须是
glfwInit
,该函数必须在其他任何函数调用之前调用; - 如果使用
glfwInit
初始化了运行环境,必须使用glfwTerminate
释放。
#include
#include
#include
#include
int main(int argc, char const *argv[]){
// 初始化
int re = glfwInit();
if(re == GLFW_FALSE){
printf("初始化GLFW环境失败!\n");
exit(-1);
}
printf("初始化GLFW环境成功!\n");
//程序结束前一定要释放
glfwTerminate();
printf("释放退出!\n");
return 0;
}
// 编译命令:g++ -omain gl01_01_init_terminate.cpp -lglfw
2. 初始化前的提示(hint)
- 初始化提示在glfwInit之前设置,并影响库的行为,直到终止。提示使用glfwInitHint设置。
#include
#include
#include
#include
int main(int argc, char const *argv[]){
glfwInitHint(GLFW_JOYSTICK_HAT_BUTTONS, GLFW_FALSE); // 游戏杆作为按钮
glfwInitHint(GLFW_COCOA_MENUBAR, GLFW_TRUE); //菜单条
glfwInitHint(GLFW_COCOA_CHDIR_RESOURCES, GLFW_FALSE); //切换到资源路径
// 初始化
int re = glfwInit();
if(re == GLFW_FALSE){
printf("初始化GLFW环境失败!\n");
exit(-1);
}
printf("初始化GLFW环境成功!\n");
//程序结束前一定要释放
glfwTerminate();
printf("释放退出!\n");
return 0;
}
// 编译命令:g++ -omain gl01_02_init_hint.cpp -lglfw
3. 错误处理
- GLFW提供两种错误处理方式:
- 使用函数glfwGetError返回错误代码,货返返回错误描述。
- 设置错误回调函数,在错误发生时,回调函数得到调用,从而错误得到处理的机会。
3.1. 错误信息获取
- 使用返回值返回错误码;
- 使用参数返回错误描述;
#include
#include
#include
#include
int main(int argc, char const *argv[]){
glfwInitHint(GLFW_JOYSTICK_HAT_BUTTONS, GLFW_FALSE); // 游戏杆作为按钮
// 处理错误
int err_code = glfwGetError(NULL);
if (err_code == GLFW_NO_ERROR){
printf("Hint没有发生错误!\n");
}
else{
printf("Hint发生错误:%d\n", err_code);
exit(-1);
}
glfwInitHint(GLFW_COCOA_MENUBAR, GLFW_TRUE); //菜单条
glfwInitHint(GLFW_COCOA_CHDIR_RESOURCES, GLFW_FALSE); //切换到资源路径
const char *msg;
err_code = glfwGetError(&msg);
if (err_code == GLFW_NO_ERROR){
printf("Hint没有发生错误:%s\n", msg);
}
else{
printf("Hint发生错误:%s\n", msg);
exit(-1);
}
// 初始化
int re = glfwInit();
if(re == GLFW_FALSE){
printf("初始化GLFW环境失败!\n");
exit(-1);
}
printf("初始化GLFW环境成功!\n");
//程序结束前一定要释放
glfwTerminate();
printf("释放退出!\n");
return 0;
}
// 编译命令:g++ -omain gl01_03_error.cpp -lglfw
3.2. 错误回调函数
- 错误回调函数与上面方式差不多,差别在于错误发生时候,回调函数会被自动调用。
- 回调函数类型为:
typedef void (* GLFWerrorfun)(int,const char*);
#include
#include
#include
#include
void error_callback(int code , const char *msg){
// 处理错误
if (code == GLFW_NO_ERROR){
printf("Hint没有发生错误:%d:%s\n", code, msg);
}
else{
printf("Hint发生错误:%d:%s\n", code, msg);
exit(-1);
}
}
int main(int argc, char const *argv[]){
// 设置错误回调:typedef void (* GLFWerrorfun)(int,const char*)
// 返回原来的错误回调函数
GLFWerrorfun old_callback = glfwSetErrorCallback(error_callback);
printf("旧回调函数:%p\n", (void*)old_callback);
glfwInitHint(45, GLFW_FALSE); // 故意构造一个错误
glfwInitHint(GLFW_COCOA_MENUBAR, GLFW_TRUE); //菜单条
glfwInitHint(GLFW_COCOA_CHDIR_RESOURCES, GLFW_FALSE); //切换到资源路径
// 初始化
int re = glfwInit();
if(re == GLFW_FALSE){
printf("初始化GLFW环境失败!\n");
exit(-1);
}
printf("初始化GLFW环境成功!\n");
//程序结束前一定要释放
glfwTerminate();
printf("释放退出!\n");
return 0;
}
// 编译命令:g++ -omain gl01_04_error_callback.cpp -lglfw
4. 版本管理
4.1. 编译时版本
- 编译时版本通过宏提供;
- GLFW_VERSION_MAJOR,
- GLFW_VERSION_MINOR,
- GLFW_VERSION_REVISION
4.2. 运行时版本
- 使用两个函数获取:
- 数字:glfwGetVersion
- 字符串:glfwGetVersionString
4.3. 代码
#include
#include
#include
#include
int main(int argc, char const *argv[]){
// 获取编译时版本号
printf("编译时版本:%d.%d.%d\n", GLFW_VERSION_MAJOR, GLFW_VERSION_MINOR, GLFW_VERSION_REVISION);
// 获取运行时版本号
int major, minor,revision;
glfwGetVersion(&major, &minor, &revision);
printf("版本:%d.%d.%d\n", major, minor,revision); // 输出:3.3.0
printf("字符串版本:%s\n", glfwGetVersionString()); // 输出:3.3.0 Cocoa NSGL EGL OSMesa dynamic
return 0;
}
// 编译命令:g++ -omain gl01_05_version.cpp -lglfw
三. 附录
1.宏定义
#define GLFW_TRUE 1
#define GLFW_FALSE 0
#define GLFW_JOYSTICK_HAT_BUTTONS 0x00050001
#define GLFW_COCOA_CHDIR_RESOURCES 0x00051001
#define GLFW_COCOA_MENUBAR 0x00051002
2. 类型定义
typedef void(* GLFWerrorfun) (int, const char *)
3. 函数定义
int glfwInit (void)
void glfwTerminate (void)
void glfwInitHint (int hint, int value)
void glfwGetVersion (int *major, int *minor, int *rev)
const char * glfwGetVersionString (void)
int glfwGetError (const char **description)
GLFWerrorfun glfwSetErrorCallback (GLFWerrorfun cbfun)