雖然OpenGL ES一般印象是給嵌入式裝置使用,以前貼過這張圖,這類3D API除了顯示晶片以外作業(yè)系統(tǒng)也要配合,只要作業(yè)系統(tǒng)有把它加入系統(tǒng)API就可以使用,Linux也有OpenGL ES的API所以也能用。

OpenGL ES把OpenGL一些以前有用但現(xiàn)在已過時的功能刪除,函式和常數(shù)名稱和OpenGL相同,所以大部分程式碼可以不用修改轉(zhuǎn)成OpenGL ES,除了跟作業(yè)系統(tǒng)打交道的部分。筆者有考慮把目前製作中遊戲從GLX改成EGL+GLES,改寫應(yīng)該不難,程式會比GLX好看,而且以後轉(zhuǎn)移到Wayland或Android比較方便。
程式教學(xué)一覽
有些在「【程式】OpenGL 3.3初始化(X Window)」提過的就不重覆寫了。
Fedora 40要裝這個套件:mesa-libEGL-devel。Mint的套件名稱是libegl1-mesa-dev。
本篇目標版本是OpenGL ES 3.0,功能大致與OpenGL 3.3相同。
參考這篇的程式碼
https://registry.khronos.org/EGL/extensions/KHR/EGL_KHR_platform_x11.txt
之前「建立視窗~把視窗指定為OpenGL畫布」要用GLX這個函式庫,本篇則是用EGL。
儲存global狀態(tài)的變數(shù)有EGLDisplay、EGLContext、EGLSurface三個,其中EGLDisplay和EGLSurface是把平臺原生物件包裝一層,增加一些GLES用的屬性。在X Window這兩者是將Display*和Window包裝,在Windows則是HDC和HWND。
查egl.h會發(fā)現(xiàn)三者都是將void*取另一個名稱,這些是不透明的物件,外部看不到內(nèi)容,只能呼叫函式由函式庫內(nèi)部操作。
main()的內(nèi)容跟GLX篇幾乎一樣,就不解釋了。
initGLESAndCreateWindow()是跟GLX不同的主要部分。EGL的目標是不同平臺儘量使用相同的函式和常數(shù)名稱。相較於glX很多函式需要傳入X Window的Display*和Window,EGL只有eglGetDisplay()和eglCreateWindowSurface()需要傳入平臺相關(guān)變數(shù),產(chǎn)生出EGLDisplay和EGLSurface之後就使用這兩個。
第一步是用eglGetDisplay()和eglInitialize()產(chǎn)生EGLDisplay物件。eglInitialize()第二、三參數(shù)傳回這臺裝置上的EGL版本,不需要的話可填NULL。
第二步類似GLX的fbConfig,要設(shè)定framebuffer的格式,如RGBA各幾bits、有沒有depth buffer和stencil buffer。用一個整數(shù)陣列設(shè)定格式,鍵與值交替,最後用一個鍵EGL_NONE結(jié)束。把陣列傳入eglChooseConfig(),符合條件的EGLConfig會用第三參數(shù)傳回,可能有不只一個,第三參數(shù)填一個陣列,第四參數(shù)填陣列長度就能取得多個,但本篇只取一個。
有哪些屬性可以設(shè)定看eglChooseConfig()的說明。
eglChooseConfig - EGL Reference Pages
EGLConfig是不透明物件,用eglGetConfigAttrib()取得config ID並用printf()印出,config ID是什麼意思在下面說明。第三步用eglCreateContext()產(chǎn)生EGLContext,跟eglChooseConfig()一樣用EGL_NONE結(jié)束的陣列指定GLES版本。
建視窗時跟GLX一樣要讓三個物件的framebuffer格式配合:OpenGL ES、視窗、螢?zāi)弧R暣暗母袷奖仨毟鶪LES相同,方法是從EGLContext取得visual ID並用它產(chǎn)生一個XVisualInfo物件。螢?zāi)坏母袷讲灰欢ê鸵暣跋嗤a(chǎn)生Colormap物件用來轉(zhuǎn)換。最後把visual和Colormap傳入XCreateWindow()產(chǎn)生視窗。Colormap物件必須一直留著,等視窗被刪除後才能刪除。
最後用eglCreateWindowSurface()產(chǎn)生EGLSurface物件,第四參數(shù)用整數(shù)陣列設(shè)定屬性,但本篇沒用到。用eglMakeCurrent()設(shè)定目前要用的context和surface(GLES可以建立複數(shù)個context或surface看情況切換)。設(shè)定vsync的函式是eglSwapInterval()。
除了eglChooseConfig()以外,本段提到的函式的說明文件:
eglGetDisplay()
eglInitialize()
eglGetConfigAttrib()
eglCreateContext()
eglCreateWindowSurface()
eglMakeCurrent()
eglSwapInterval()
此外還有這兩個函式存在。
eglGetPlatformDisplay()
eglCreatePlatformWindowSurface()
是EGL 1.5版新增的,eglGetDisplay()和eglCreateWindowSurface()則是EGL最初的版本就有。
這兩個做的事跟GLX版一樣,只是函式名稱不同。
本篇還沒有寫shader,GLES的shader要把開頭的「#version 330」改成「#version 300 es」。
用這個指令build:
用命令列執(zhí)行才看得到prinf()的輸出。執(zhí)行的樣子:

這個例子GLES的config ID是0x01,視窗與螢?zāi)坏膙isual ID是0x49(十六進位),代表什麼意思在下面「eglinfo」一節(jié)說明。
vsync與擴充函式的注意事項跟GLX那篇相同故不再贅述。EGL用來取得擴充函式的函式是eglGetProcAddress()。
想查OpenGL ES哪一版有哪些功能,方法一是在以下網(wǎng)址查。
OpenGL ES 3.0 Reference Pages
OpenGL ES 3.1 Reference Pages
OpenGL ES 3.2 Reference Pages
方法二是查自己電腦裡的C語言header。例如筆者寫這篇時用的Fedora 40是「/usr/include/GLES3」裡的gl3.h、gl31.h和gl32.h。
看函式或常數(shù)名稱有沒有列在裡面,有就是有功能,沒有就是沒功能。例如看glCreateShader()的說明,3.0版參數(shù)可填GL_VERTEX_SHADER和GL_FRAGMENT_SHADER,3.1版增加了GL_COMPUTE_SHADER,可得知compute shader是在3.1版加入的。
GLES最終版本是3.2,功能接近OpenGL 4.3,之後就用Vulkan取代。
- eglinfo -
查看EGL與GLES資訊的軟體,F(xiàn)edora 40要安裝這個套件:egl-utils。
此套件有另一個程式es2_info,但它只能取得目前平臺的GLES擴充字串,用途跟eglinfo重複。
在命令列直接打eglinfo會列出很大量的資訊,用「eglinfo>a.txt」把資訊輸出到文字檔比較好查?!竐glinfo -h」可以查命令列參數(shù)的用法。
首先它會列出很多平臺(platform),像筆者的情況有這些:
GBM platform
Wayland platform
X11 platform
Surfaceless platform
Device platform
其中GBM platform只有這兩行,表示這臺電腦不支援GBM平臺。
本篇用的是X11平臺,先找到「X11 platform:」的部分再看內(nèi)容。
OpenGL core、OpenGL compatibility、OpenGL ES這些的extensions內(nèi)容跟glxinfo顯示的一樣,就請看GLX那篇。
新的東西是Configurations。
程式執(zhí)行時印出config ID是0x01,對照這個表就能查到framebuffer格式。
這個程式不會列出visual資訊,要用glxinfo才看得到。

OpenGL ES把OpenGL一些以前有用但現(xiàn)在已過時的功能刪除,函式和常數(shù)名稱和OpenGL相同,所以大部分程式碼可以不用修改轉(zhuǎn)成OpenGL ES,除了跟作業(yè)系統(tǒng)打交道的部分。筆者有考慮把目前製作中遊戲從GLX改成EGL+GLES,改寫應(yīng)該不難,程式會比GLX好看,而且以後轉(zhuǎn)移到Wayland或Android比較方便。
程式教學(xué)一覽
有些在「【程式】OpenGL 3.3初始化(X Window)」提過的就不重覆寫了。
Fedora 40要裝這個套件:mesa-libEGL-devel。Mint的套件名稱是libegl1-mesa-dev。
本篇目標版本是OpenGL ES 3.0,功能大致與OpenGL 3.3相同。
參考這篇的程式碼
https://registry.khronos.org/EGL/extensions/KHR/EGL_KHR_platform_x11.txt
#include<EGL/egl.h> #include<GLES3/gl3.h> #include<X11/Xutil.h> //使用XGetVisualInfo() #include<stdio.h> #include<unistd.h> //使用usleep() const int WINDOW_W=200,WINDOW_H=200; //OpenGL ES必要物件 Display* dsp; Window window; Colormap cmap; EGLDisplay eglDsp; EGLContext context; EGLSurface surface; //畫面顏色 float color[]={0,0,0,1}; //這三個函式在下面說明 static Window initGLESAndCreateWindow(); static void nextFrame(); static void deinitGLES(); int main(){ dsp = XOpenDisplay( NULL ); window=initGLESAndCreateWindow(); if(!window){ printf("Can not initialize OpenGL ES\n"); return 0; } //設(shè)標題 XStoreName(dsp, window, "title"); //設(shè)定事件mask Atom wmDelete = XInternAtom(dsp, "WM_DELETE_WINDOW", False); XSetWMProtocols(dsp, window, &wmDelete, 1); XMapWindow(dsp, window); //接收事件 XEvent evt; int isEnd=0; while(!isEnd){ while(XPending(dsp)){ XNextEvent(dsp, &evt); switch(evt.type){ case ClientMessage: if(evt.xclient.data.l[0]== wmDelete){ isEnd=1; } break; } } //正式寫遊戲時,遊戲邏輯放在此處 nextFrame(); usleep(16000); } deinitGLES(); XDestroyWindow(dsp, window); XFreeColormap(dsp, cmap); XFlush(dsp); XCloseDisplay(dsp); return 0; } |
儲存global狀態(tài)的變數(shù)有EGLDisplay、EGLContext、EGLSurface三個,其中EGLDisplay和EGLSurface是把平臺原生物件包裝一層,增加一些GLES用的屬性。在X Window這兩者是將Display*和Window包裝,在Windows則是HDC和HWND。
查egl.h會發(fā)現(xiàn)三者都是將void*取另一個名稱,這些是不透明的物件,外部看不到內(nèi)容,只能呼叫函式由函式庫內(nèi)部操作。
main()的內(nèi)容跟GLX篇幾乎一樣,就不解釋了。
//X11必須先建EGLContext再建視窗 static Window initGLESAndCreateWindow(){ eglDsp=eglGetDisplay(dsp); if(eglDsp==EGL_NO_DISPLAY){ return 0; } eglInitialize(eglDsp,NULL,NULL); const int configAttrib[]={ EGL_RED_SIZE,8, EGL_GREEN_SIZE,8, EGL_BLUE_SIZE,8,EGL_ALPHA_SIZE,8, EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_SURFACE_TYPE, EGL_WINDOW_BIT, //這個跟預(yù)設(shè)值相同,可不填 EGL_NONE, }; int numConfig; EGLConfig eglConfig; EGLBoolean ret=eglChooseConfig(eglDsp, configAttrib, &eglConfig,1,&numConfig); if(ret==EGL_FALSE){ eglTerminate(eglDsp); return 0; } //印出EGLConfig編號 EGLint configID; eglGetConfigAttrib(eglDsp, eglConfig, EGL_CONFIG_ID, &configID); printf("config ID %x\n",configID); //產(chǎn)生OpenGLES 3.0 context const int contextAttrib[]={ EGL_CONTEXT_MAJOR_VERSION, 3,EGL_CONTEXT_MINOR_VERSION,0, EGL_NONE, }; context=eglCreateContext(eglDsp, eglConfig, NULL, contextAttrib); if(context==0){ eglTerminate(eglDsp); return 0; } //取得這個EGLConfig的visual,用它建立視窗 EGLint visualID; eglGetConfigAttrib(eglDsp, eglConfig, EGL_NATIVE_VISUAL_ID, &visualID); XVisualInfo vInfoTemplate; vInfoTemplate.visualid=visualID; int numVisual; XVisualInfo* vInfo=XGetVisualInfo(dsp, VisualIDMask,&vInfoTemplate,&numVisual); XSetWindowAttributes windowAttr; cmap = XCreateColormap(dsp, DefaultRootWindow(dsp), vInfo->visual,AllocNone); windowAttr.colormap = cmap; window = XCreateWindow(dsp, DefaultRootWindow(dsp), 0,0,WINDOW_W,WINDOW_H, 0, vInfo->depth, InputOutput, vInfo->visual, CWColormap, &windowAttr); XFree(vInfo); //印出visual編號 int defaultVisualID=XVisualIDFromVisual(DefaultVisual(dsp, DefaultScreen(dsp))); printf("defaultVisual:%x windowVisual:%x\n",defaultVisualID,visualID); surface=eglCreateWindowSurface(eglDsp,eglConfig, window, NULL); eglMakeCurrent(eglDsp, surface,surface, context); //設(shè)定Vsync eglSwapInterval(eglDsp, 0); return window; } |
第一步是用eglGetDisplay()和eglInitialize()產(chǎn)生EGLDisplay物件。eglInitialize()第二、三參數(shù)傳回這臺裝置上的EGL版本,不需要的話可填NULL。
第二步類似GLX的fbConfig,要設(shè)定framebuffer的格式,如RGBA各幾bits、有沒有depth buffer和stencil buffer。用一個整數(shù)陣列設(shè)定格式,鍵與值交替,最後用一個鍵EGL_NONE結(jié)束。把陣列傳入eglChooseConfig(),符合條件的EGLConfig會用第三參數(shù)傳回,可能有不只一個,第三參數(shù)填一個陣列,第四參數(shù)填陣列長度就能取得多個,但本篇只取一個。
有哪些屬性可以設(shè)定看eglChooseConfig()的說明。
eglChooseConfig - EGL Reference Pages
EGLConfig是不透明物件,用eglGetConfigAttrib()取得config ID並用printf()印出,config ID是什麼意思在下面說明。第三步用eglCreateContext()產(chǎn)生EGLContext,跟eglChooseConfig()一樣用EGL_NONE結(jié)束的陣列指定GLES版本。
建視窗時跟GLX一樣要讓三個物件的framebuffer格式配合:OpenGL ES、視窗、螢?zāi)弧R暣暗母袷奖仨毟鶪LES相同,方法是從EGLContext取得visual ID並用它產(chǎn)生一個XVisualInfo物件。螢?zāi)坏母袷讲灰欢ê鸵暣跋嗤a(chǎn)生Colormap物件用來轉(zhuǎn)換。最後把visual和Colormap傳入XCreateWindow()產(chǎn)生視窗。Colormap物件必須一直留著,等視窗被刪除後才能刪除。
最後用eglCreateWindowSurface()產(chǎn)生EGLSurface物件,第四參數(shù)用整數(shù)陣列設(shè)定屬性,但本篇沒用到。用eglMakeCurrent()設(shè)定目前要用的context和surface(GLES可以建立複數(shù)個context或surface看情況切換)。設(shè)定vsync的函式是eglSwapInterval()。
除了eglChooseConfig()以外,本段提到的函式的說明文件:
eglGetDisplay()
eglInitialize()
eglGetConfigAttrib()
eglCreateContext()
eglCreateWindowSurface()
eglMakeCurrent()
eglSwapInterval()
此外還有這兩個函式存在。
eglGetPlatformDisplay()
eglCreatePlatformWindowSurface()
是EGL 1.5版新增的,eglGetDisplay()和eglCreateWindowSurface()則是EGL最初的版本就有。
static void nextFram(){ color[0]+=1.0/60; if (color[0]>1){ color[0]=0; } glClearColor(color[0],color[1],color[2],color[3]); glClear(GL_COLOR_BUFFER_BIT); eglSwapBuffers(eglDsp, surface); } static void deinitGLES(){ eglDestroyContext(eglDsp, context); eglDestroySurface(eglDsp, surface); eglTerminate(eglDsp); } |
本篇還沒有寫shader,GLES的shader要把開頭的「#version 330」改成「#version 300 es」。
用這個指令build:
gcc simplegles.c -o simplegles -Os -s -lX11 -lEGL -lGLESv2 |
這個例子GLES的config ID是0x01,視窗與螢?zāi)坏膙isual ID是0x49(十六進位),代表什麼意思在下面「eglinfo」一節(jié)說明。
vsync與擴充函式的注意事項跟GLX那篇相同故不再贅述。EGL用來取得擴充函式的函式是eglGetProcAddress()。
想查OpenGL ES哪一版有哪些功能,方法一是在以下網(wǎng)址查。
OpenGL ES 3.0 Reference Pages
OpenGL ES 3.1 Reference Pages
OpenGL ES 3.2 Reference Pages
方法二是查自己電腦裡的C語言header。例如筆者寫這篇時用的Fedora 40是「/usr/include/GLES3」裡的gl3.h、gl31.h和gl32.h。
看函式或常數(shù)名稱有沒有列在裡面,有就是有功能,沒有就是沒功能。例如看glCreateShader()的說明,3.0版參數(shù)可填GL_VERTEX_SHADER和GL_FRAGMENT_SHADER,3.1版增加了GL_COMPUTE_SHADER,可得知compute shader是在3.1版加入的。
GLES最終版本是3.2,功能接近OpenGL 4.3,之後就用Vulkan取代。
- eglinfo -
查看EGL與GLES資訊的軟體,F(xiàn)edora 40要安裝這個套件:egl-utils。
此套件有另一個程式es2_info,但它只能取得目前平臺的GLES擴充字串,用途跟eglinfo重複。
在命令列直接打eglinfo會列出很大量的資訊,用「eglinfo>a.txt」把資訊輸出到文字檔比較好查?!竐glinfo -h」可以查命令列參數(shù)的用法。
首先它會列出很多平臺(platform),像筆者的情況有這些:
GBM platform
Wayland platform
X11 platform
Surfaceless platform
Device platform
其中GBM platform只有這兩行,表示這臺電腦不支援GBM平臺。
GBM platform: eglinfo: eglInitialize failed |
OpenGL core、OpenGL compatibility、OpenGL ES這些的extensions內(nèi)容跟glxinfo顯示的一樣,就請看GLX那篇。
新的東西是Configurations。
Configurations: bf lv colorbuffer dp st ms vis cav bi renderable supported id sz l r g b a th cl ns b id eat nd gl es es2 vg surfaces --------------------------------------------------------------------- 0x01 32 0 8 8 8 8 0 0 0 0 0x49TC a y y y win,pb,pix 0x02 32 0 8 8 8 8 16 0 0 0 0x49TC a y y y win,pb,pix 0x03 32 0 8 8 8 8 24 0 0 0 0x49TC a y y y win,pb,pix …… |
這個程式不會列出visual資訊,要用glxinfo才看得到。