本文主要說(shuō)明OpenGL的編程模式:
??1. OpenGK環(huán)境與上下文初始化现恼;
??2. 錯(cuò)誤處理凛澎;
??3. 版本管理;
- 說(shuō)明
- GLFW與GLEW無(wú)關(guān)适掰,可以獨(dú)立調(diào)用孕荠;主要負(fù)責(zé)UI部分。
一. GLFW的官方文檔
1. 官方參考
https://www.glfw.org/docs/latest/modules.html
2. 在線(xiàn)教程
https://www.glfw.org/docs/latest/
- 教程分成6個(gè)主題來(lái)說(shuō)明:
- 初始化:其實(shí)就是編程模式攻谁;
- Window:創(chuàng)建窗體組件;
- Context:OpenGL與OpenGL ES的上下文環(huán)境弯予;
- Vulkan:一個(gè)跨平臺(tái)的2D和3D繪圖應(yīng)用程序接口(API)戚宦;
- Monitor:監(jiān)視器與視頻工作模式;
- Input:交互事件處理:鍵盤(pán)與鼠標(biāo)交互輸入锈嫩;
二. GLFW編程模式
1. 初始化與釋放終止
1.1. 函數(shù)說(shuō)明
-
GLFWAPI void glfwGetVersion(int* major, int* minor, int* rev);
- 函數(shù)說(shuō)明:
- 獲取運(yùn)行時(shí)GLFW的版本受楼;
- 函數(shù)參數(shù):
- 返回主版本號(hào)垦搬,副版本號(hào),修訂號(hào)
- 函數(shù)返回值:
- 無(wú)
- 說(shuō)明:
- 該函數(shù)的調(diào)用艳汽,無(wú)需調(diào)用glfwInit函數(shù)猴贰。
- 函數(shù)說(shuō)明:
-
GLFWAPI const char* glfwGetVersionString(void);
- 與上面函數(shù)一樣,只是返回字符串格式的版本號(hào)河狐;
-
GLFWAPI int glfwGetError(const char** description);
- 函數(shù)說(shuō)明:
- 用來(lái)獲取上次執(zhí)行函數(shù)發(fā)生的錯(cuò)誤米绕,某些函數(shù)沒(méi)有返回值,從而無(wú)法返回錯(cuò)誤狀態(tài)馋艺,使用該函數(shù)就非常方便栅干。
- 函數(shù)參數(shù):
- 返回錯(cuò)誤的字符串描述;需要雙指針捐祠,一個(gè)存放地址的地址碱鳞,這意味著錯(cuò)誤描述的字符串空間是函數(shù)分配的。
- 沒(méi)有錯(cuò)誤發(fā)生踱蛀,返回NULL窿给。
- 函數(shù)返回值:
- 返回上次函數(shù)執(zhí)行產(chǎn)生的錯(cuò)誤碼;
- 說(shuō)明:
- 返回GLFW_NO_ERROR表示沒(méi)有錯(cuò)誤發(fā)生率拒。GLFW_NO_ERROR的定義如下:
#define GLFW_NO_ERROR 0
- 返回GLFW_NO_ERROR表示沒(méi)有錯(cuò)誤發(fā)生率拒。GLFW_NO_ERROR的定義如下:
- 函數(shù)說(shuō)明:
- GLFWAPI GLFWerrorfun glfwSetErrorCallback(GLFWerrorfun cbfun);
- 函數(shù)說(shuō)明:
- 用來(lái)設(shè)置函數(shù)錯(cuò)誤發(fā)生時(shí)候的回調(diào)函數(shù)崩泡。
- 函數(shù)參數(shù):
- 回調(diào)函數(shù),該函數(shù)原型定義如下(兩個(gè)參數(shù)俏橘,用來(lái)傳遞錯(cuò)誤碼與錯(cuò)誤描述):
typedef void (* GLFWerrorfun)(int,const char*);
- 回調(diào)函數(shù),該函數(shù)原型定義如下(兩個(gè)參數(shù)俏橘,用來(lái)傳遞錯(cuò)誤碼與錯(cuò)誤描述):
- 函數(shù)返回值:
- 返回上次設(shè)置的錯(cuò)誤回調(diào)函數(shù)允华,上次沒(méi)有設(shè)置則返回NULL。
- 函數(shù)說(shuō)明:
-
GLFWAPI void glfwInitHint(int hint, int value);
- 函數(shù)說(shuō)明:
- 在glfwinit之前調(diào)用用來(lái)設(shè)置初始化提示寥掐,提示設(shè)置會(huì)影響初始化行為靴寂,并最終影響庫(kù)的行為,直到環(huán)境終止召耘。
- 函數(shù)參數(shù):
- int hint:設(shè)置提示的類(lèi)型百炬;
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:設(shè)置提示的值;支持的值:
- GLFW_TRUE
- GLFW_FALSE
3. 函數(shù)返回值:
- 無(wú)
4. 說(shuō)明:
- 某些平臺(tái)有自己特殊的提示設(shè)置污它。
- 可能產(chǎn)生的錯(cuò)誤有:GLFW_INVALID_ENUM 與 GLFW_INVALID_VALUE剖踊;
- int hint:設(shè)置提示的類(lèi)型百炬;
- 函數(shù)說(shuō)明:
-
GLFWAPI int glfwInit(void);
- 函數(shù)說(shuō)明:
此函數(shù)初始化glfw庫(kù)。在大多數(shù)glfw函數(shù)可以使用之前衫贬,必須初始化glfw德澈,并且在應(yīng)用程序終止之前,應(yīng)該終止glfw固惯,以便釋放初始化期間或之后分配的任何資源梆造。
如果此函數(shù)失敗,則在返回之前調(diào)用glfwterminate函數(shù)葬毫。
如果成功镇辉,則應(yīng)在應(yīng)用程序退出之前調(diào)用glfwterminate函數(shù)屡穗。
如果還函數(shù)已經(jīng)成功初始化,則再此調(diào)用將立即返回GLFW_TRUE忽肛。
此函數(shù)將應(yīng)用程序的當(dāng)前目錄更改為應(yīng)用程序包的“Contents/Resources”子目錄(如果存在)村砂。這可以通過(guò)GLFW_COCOA_CHDIR_RESOURCES的init hint禁用。
- 函數(shù)參數(shù)
- 無(wú)
- 函數(shù)返回值:
- 成功返回GLFW_TRUE
- 失敗返回GLFW_FALSE
- 說(shuō)明:
- GLFW_TRUE與GLFW_FALSE的定義:
#define GLFW_TRUE 1
#define GLFW_FALSE 0
- 可能得錯(cuò)誤:GLFW_PLATFORM_ERROR屹逛,使用函數(shù)error_handling處理础废。
- GLFW_TRUE與GLFW_FALSE的定義:
- 函數(shù)說(shuō)明:
- GLFWAPI void glfwTerminate(void);
- 函數(shù)說(shuō)明:
- 此函數(shù)銷(xiāo)毀所有剩余的窗口和光標(biāo),恢復(fù)任何修改過(guò)的gamma漸變并釋放任何其他分配的資源煎源。 調(diào)用此函數(shù)后色迂,必須再次成功調(diào)用glfwInit,才能使用大多數(shù)glfw函數(shù)手销。
- 如果glfw已成功初始化歇僧,則應(yīng)在應(yīng)用程序退出之前調(diào)用此函數(shù)。
- 如果初始化失敗锋拖,則無(wú)需調(diào)用此函數(shù)诈悍,因?yàn)樗诜祷豧ailure之前由glfwinit調(diào)用。
- 該函數(shù)不要在回調(diào)函數(shù)中調(diào)用兽埃;
- 該函數(shù)只能在主線(xiàn)程中調(diào)用侥钳;
- 函數(shù)參數(shù):
- 無(wú)
- 函數(shù)返回值:
- 無(wú)
- 函數(shù)說(shuō)明:
1.2. 關(guān)于GLFWAPI
-
GLFWAPI是一個(gè)使用#define定義的宏,用來(lái)說(shuō)明函數(shù)來(lái)自DLL庫(kù)柄错。
__declspec(dllexport)
-
說(shuō)明:
dllexport是在這些類(lèi)舷夺、函數(shù)以及數(shù)據(jù)的申明的時(shí)候使用珍策。用他表明這些東西可以被外部函數(shù)使用笼吟,即(dllexport)是把 DLL中的相關(guān)代碼(類(lèi),函數(shù)先朦,數(shù)據(jù))暴露出來(lái)為其他應(yīng)用程序使用颂跨。
使用了(dllexport)關(guān)鍵字敢伸,相當(dāng)于聲明了緊接在(dllexport)關(guān)鍵字后面的相關(guān)內(nèi)容是可以為其他程序使用的。
dllimport是在外部程序需要使用DLL內(nèi)相關(guān)內(nèi)容時(shí)使用的關(guān)鍵字恒削。當(dāng)一個(gè)外部程序要使用DLL 內(nèi)部代碼(類(lèi)池颈,函數(shù),全局變量)時(shí)钓丰,只需要在程序內(nèi)部使用(dllimport)關(guān)鍵字聲明需要使用的代碼就可以了躯砰,即(dllimport)關(guān)鍵字是在外部程序需要使用DLL內(nèi)部相關(guān)內(nèi)容的時(shí)候才使用。(dllimport)作用是把DLL中的相關(guān)代碼插入到應(yīng)用程序中携丁。
_declspec(dllexport)與_declspec(dllimport)是相互呼應(yīng)琢歇,只有在DLL內(nèi)部用dllexport作了聲明,才能在外部函數(shù)中用dllimport導(dǎo)入相關(guān)代碼。
/* 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. 運(yùn)行初始化與釋放
- 第一個(gè)運(yùn)行的函數(shù)必須是
glfwInit
矿微,該函數(shù)必須在其他任何函數(shù)調(diào)用之前調(diào)用; - 如果使用
glfwInit
初始化了運(yùn)行環(huán)境尚揣,必須使用glfwTerminate
釋放涌矢。
#include <GLFW/glfw3.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char const *argv[]){
// 初始化
int re = glfwInit();
if(re == GLFW_FALSE){
printf("初始化GLFW環(huán)境失敗!\n");
exit(-1);
}
printf("初始化GLFW環(huán)境成功!\n");
//程序結(jié)束前一定要釋放
glfwTerminate();
printf("釋放退出!\n");
return 0;
}
// 編譯命令:g++ -omain gl01_01_init_terminate.cpp -lglfw
2. 初始化前的提示(hint)
- 初始化提示在glfwInit之前設(shè)置快骗,并影響庫(kù)的行為娜庇,直到終止。提示使用glfwInitHint設(shè)置方篮。
#include <GLFW/glfw3.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
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環(huán)境失敗!\n");
exit(-1);
}
printf("初始化GLFW環(huán)境成功!\n");
//程序結(jié)束前一定要釋放
glfwTerminate();
printf("釋放退出名秀!\n");
return 0;
}
// 編譯命令:g++ -omain gl01_02_init_hint.cpp -lglfw
3. 錯(cuò)誤處理
- GLFW提供兩種錯(cuò)誤處理方式:
- 使用函數(shù)glfwGetError返回錯(cuò)誤代碼,貨返返回錯(cuò)誤描述藕溅。
- 設(shè)置錯(cuò)誤回調(diào)函數(shù)匕得,在錯(cuò)誤發(fā)生時(shí),回調(diào)函數(shù)得到調(diào)用巾表,從而錯(cuò)誤得到處理的機(jī)會(huì)汁掠。
3.1. 錯(cuò)誤信息獲取
- 使用返回值返回錯(cuò)誤碼;
- 使用參數(shù)返回錯(cuò)誤描述集币;
#include <GLFW/glfw3.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char const *argv[]){
glfwInitHint(GLFW_JOYSTICK_HAT_BUTTONS, GLFW_FALSE); // 游戲桿作為按鈕
// 處理錯(cuò)誤
int err_code = glfwGetError(NULL);
if (err_code == GLFW_NO_ERROR){
printf("Hint沒(méi)有發(fā)生錯(cuò)誤!\n");
}
else{
printf("Hint發(fā)生錯(cuò)誤:%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沒(méi)有發(fā)生錯(cuò)誤:%s\n", msg);
}
else{
printf("Hint發(fā)生錯(cuò)誤:%s\n", msg);
exit(-1);
}
// 初始化
int re = glfwInit();
if(re == GLFW_FALSE){
printf("初始化GLFW環(huán)境失敗!\n");
exit(-1);
}
printf("初始化GLFW環(huán)境成功!\n");
//程序結(jié)束前一定要釋放
glfwTerminate();
printf("釋放退出考阱!\n");
return 0;
}
// 編譯命令:g++ -omain gl01_03_error.cpp -lglfw
3.2. 錯(cuò)誤回調(diào)函數(shù)
- 錯(cuò)誤回調(diào)函數(shù)與上面方式差不多,差別在于錯(cuò)誤發(fā)生時(shí)候鞠苟,回調(diào)函數(shù)會(huì)被自動(dòng)調(diào)用乞榨。
- 回調(diào)函數(shù)類(lèi)型為:
typedef void (* GLFWerrorfun)(int,const char*);
#include <GLFW/glfw3.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
void error_callback(int code , const char *msg){
// 處理錯(cuò)誤
if (code == GLFW_NO_ERROR){
printf("Hint沒(méi)有發(fā)生錯(cuò)誤:%d:%s\n", code, msg);
}
else{
printf("Hint發(fā)生錯(cuò)誤:%d:%s\n", code, msg);
exit(-1);
}
}
int main(int argc, char const *argv[]){
// 設(shè)置錯(cuò)誤回調(diào):typedef void (* GLFWerrorfun)(int,const char*)
// 返回原來(lái)的錯(cuò)誤回調(diào)函數(shù)
GLFWerrorfun old_callback = glfwSetErrorCallback(error_callback);
printf("舊回調(diào)函數(shù):%p\n", (void*)old_callback);
glfwInitHint(45, GLFW_FALSE); // 故意構(gòu)造一個(gè)錯(cuò)誤
glfwInitHint(GLFW_COCOA_MENUBAR, GLFW_TRUE); //菜單條
glfwInitHint(GLFW_COCOA_CHDIR_RESOURCES, GLFW_FALSE); //切換到資源路徑
// 初始化
int re = glfwInit();
if(re == GLFW_FALSE){
printf("初始化GLFW環(huán)境失敗!\n");
exit(-1);
}
printf("初始化GLFW環(huán)境成功!\n");
//程序結(jié)束前一定要釋放
glfwTerminate();
printf("釋放退出!\n");
return 0;
}
// 編譯命令:g++ -omain gl01_04_error_callback.cpp -lglfw
4. 版本管理
4.1. 編譯時(shí)版本
- 編譯時(shí)版本通過(guò)宏提供当娱;
- GLFW_VERSION_MAJOR,
- GLFW_VERSION_MINOR,
- GLFW_VERSION_REVISION
4.2. 運(yùn)行時(shí)版本
- 使用兩個(gè)函數(shù)獲瘸约取:
- 數(shù)字:glfwGetVersion
- 字符串:glfwGetVersionString
4.3. 代碼
#include <GLFW/glfw3.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char const *argv[]){
// 獲取編譯時(shí)版本號(hào)
printf("編譯時(shí)版本:%d.%d.%d\n", GLFW_VERSION_MAJOR, GLFW_VERSION_MINOR, GLFW_VERSION_REVISION);
// 獲取運(yùn)行時(shí)版本號(hào)
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. 類(lèi)型定義
typedef void(* GLFWerrorfun) (int, const char *)
3. 函數(shù)定義
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)