轉自https://www.2cto.com/kf/201806/752471.html
什麼是OpenGL?
Open Graphics Library (OpenGL) is a cross-language, cross-platform application programming interface (API) for rendering 2D and 3D vector graphics. The API is typically used to interact with a graphics processing unit (GPU), to achieve hardware-accelerated rendering.
OpenGL是和編程語言,平臺無關的一套interface,主要是爲了rendering 2D和3D圖形等。一般這套接口是用來和GPU進行交互的,使用GPU進行rendering硬件加速。
什麼是OpenGL ES?
Android includes support for high performance 2D and 3D graphics with the Open Graphics Library (OpenGL), specifically, the OpenGL ES API. OpenGL is a cross-platform graphics API that specifies a standard software interface for 3D graphics processing hardware. OpenGL ES is a flavor of the OpenGL specification intended for embedded devices.
OpenGL ES就是專門爲嵌入式設備設計的,當然售後機也是嵌入式,那麼OpenGL ES和OpenGL中的函數接口肯定有些是不一樣的,因爲嵌入式設備和pc等的硬件處理能力還是有差距的,不然手機卡死了。
既然OpenGL ES只是一組函數接口,那麼如何使用呢?我們肯定首先要去實現這些函數接口,而android提供了兩種類型的實現:軟件實現,硬件實現。
a, 硬件實現,前面提到這組函數接口主要是爲了和GPU這個硬件進行打交道的。所以各個硬件廠商會提供相關的實現,例如高通平臺的adreno解決方案;
b,軟件實現,android也提供了一套OpenGL ES的軟件實現,就是說不用GPU了,完全用軟件實現畫圖的相關功能,也就是libagl,代碼在frameworks\native\opengl\libagl,其makefile中,
1 2 3 |
|
到此,已經有了OpenGL ES的具體實現,但是由於其實現的平臺無關係,所以在android上還不能使用,必須藉助EGL。
EGL
EGL - Native Platform Interface
EGL is an interface between Khronos rendering APIs such as OpenGL ES or OpenVG and the underlying native platform window system.
It handles graphics context management, surface/buffer binding, and rendering synchronization and enables high-performance, accelerated, mixed-mode 2D and 3D rendering using other Khronos APIs.
EGL,它是圖形渲染API(如OpenGL ES)與本地平臺窗口系統的一層接口,保證了OpenGL ES的平臺獨立性。EGL提供了若干的功能:創建rendering surface,創建graphics context,同步應用程序和本地平臺渲染API,提供對顯示設備的訪問,提供對渲染配置的管理等。
EGL提供了一種方法用於通過客戶端API和本地窗口系統進行渲染,客戶端API包括用於嵌入式系統的3D渲染器和OpenGLES,用於桌面系統的OpenGL ES的超集OpenGL,2D矢量圖形渲染器OpenVG,本地窗口系統包括Windows,X
那麼什麼是EGL?EGL是OpenGL ES和底層的native window system之間的接口,承上啓下。
EGL is a complement to OpenGL ES. EGL is used for getting surfaces to render to using functions like eglCreateWindowSurface, and you can then draw to that surface with OpenGL ES. Its role is similar to GLX/WGL/CGL.
Whether or not EGL can give you a context that supports OpenGL ES 2.0 may vary by platform, but if the Android device supports ES 2.0 and EGL, you should be able to get such a context from EGL. Take a look at the EGL_RENDERABLE_TYPE attribute and the EGL_OPENGL_ES2_BIT when requesting an EGLConfig.
在Android上,EGL完善了OpenGL ES。利用類似eglCreateWindowSurface的EGL函數可以創建surface 用來render ,有了這個surface你就能往這個surface中利用OpenGL ES函數去畫圖了。
EGL要做什麼
EGL既然做平臺和OpenGL ES的中間層,那EGL做的肯定就是和平臺息息相關的事:
創建繪圖窗口
也就是所謂的FrameBuffer,FrameBuffer可以先到到屏幕上(SurfaceView) 創建渲染環境(Context上下文)
渲染環境是指OpenGL ES的所有項目運行需要的 數據結構。如頂點,片段着色器,頂點數據矩陣
OpenGL渲染一般流程:
EGLConfig屬性
屬性 | 描述 | 默認值 |
---|---|---|
EGL_BUFFER_SIZE | 顏色緩衝器中所有組成演的的位數 | 0 |
EGL_RED_SIZE | 顏色緩衝區中紅色位數 | 0 |
EGL_LUMINANCE_SIZE | 顏色緩衝區中透明度的位數 | 0 |
EGL_ALPTHA_SIZE | 顏色緩衝區中透明位數 | 0 |
EGL_ALPTHHA_MASK_SIZE | 遮擋緩衝區透明度掩碼位數 | 0 |
EGL_BIND_TO_TEXTURE_RGB | 綁定到RGB貼圖使能爲真 | EGL_DONT_CARE |
EGL_BIND_TO_TEXTURE_RGBA | 綁定到RGBA貼圖使能爲真 | EGL_DONT_CARE |
EGL_COLOR_BUFFER_TYPE | 顏色緩衝區類型 EGL_RGB_BUFFER, 或者EGL_LUMINANCE_BUFFER | EGL_RGB_BUFFER |
EGL_CONFIG_CAVEAT | 配置有關的警告信息 | EGL_DONT_CARE |
EGL_CONFIG_ID | 唯一的 EGLConfig 標示值 | EGL_DONT_CARE |
EGL_CONFORMANT | 使用EGLConfig 創建的上下文符合要求時爲真 | - |
EGL_DEPTH_SIZE | 深度緩衝區位數 | 0 |
EGL_LEVEL | 幀緩衝區水平 | 0 |
EGL_MAX_PBUFFER_WIDTH | 使用EGLConfig 創建的PBuffer的最大寬度 | — |
EGL_MAX_PBUFFER_HEIGHT | 使用EGLConfig 創建的PBuffer最大高度 | — |
EGL_MAX_PBUFFER_PIXELS | 使用EGLConfig 創建的PBuffer最大尺寸 | — |
EGL_MAX_SWAP_INTERVAL | 最大緩衝區交換間隔 | EGL_DONT_CARE |
EGL_MIN_SWAP_INTERVAL | 最小緩衝區交換間隔 | EGL_DONT_CARE |
EGL_NATIVE_RENDERABLE | 如果操作系統渲染庫能夠使用EGLConfig 創建渲染渲染窗口 | EGL_DONT_CARE |
EGL_NATIVE_VISUAL_ID | 與操作系統通訊的可視ID句柄 | EGL_DONT_CARE |
EGL_NATIVE_VISUAL_TYPE | 與操作系統通訊的可視ID類型 | EGL_DONT_CARE |
EGL_RENDERABLE_TYPE | 渲染窗口支持的佈局組成標示符的遮擋位EGL_OPENGL_ES_BIT, EGL_OPENGL_ES2_BIT, orEGL_OPENVG_BIT that | EGL_OPENGL_ES_BIT |
EGL_SAMPLE_BUFFERS | 可用的多重採樣緩衝區位數 | 0 |
EGL_SAMPLES | 每像素多重採樣數 | 0 |
EGL_S TENCIL_SIZE | 模板緩衝區位數 | 0 |
EGL_SURFACE_TYPE | EGL 窗口支持的類型EGL_WINDOW_BIT, EGL_PIXMAP_BIT,或EGL_PBUFFER_BIT | EGL_WINDOW_BIT |
EGL_TRANSPARENT_TYPE | 支持的透明度類型 | EGL_NONE |
EGL_TRANSPARENT_RED_VALUE | 透明度的紅色解釋 | EGL_DONT_CARE |
EGL_TRANSPARENT_GRE EN_VALUE | 透明度的綠色解釋 | EGL_DONT_CARE |
EGL_TRANSPARENT_BLUE_VALUE | 透明度的蘭色解釋 | EGL_DONT_CARE |
一個簡單例子:
OpenGL的渲染是基於線程的,所以我們要創建一個GLLRenderer類繼承於HandlerThread:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 |
|
createGL()方法獲取了一個默認的顯示設備(也就是手機屏幕),初始化並返回當前系統使用的OpenGL版本(主版本+子版本),然後通過配置(主要以鍵值對的方式配置,最後由EGL_NONE結尾)得到一個EGLConfig,最後創建一個EGLContext。
destroyGL()方法則是釋放掉OpenGL的資源(主要就是EGLContext)。
render()方法中主要是渲染,這裏爲了方便把渲染的環境和渲染寫在一起並只渲染一次(我們只畫了一個三角形),前三行代碼我們創建了一個EGLSurface並設置爲當前的渲染對象,後面eglSwapBuffers()交換了顯示器和EGLSurface的顯存,也就是將我們渲染的東西放到顯示器去顯示,這樣我們就看到我們繪製的三角形了,最後就是銷燬我們創建的EGLSurface。
然後在Activity中使用它:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
|
然後我們就可以得到一個:
然後我們繼續學習:
EGL創建EGLSurface有三個方法:glCreateWindowSurface()、eglCreatePbufferSurface()和eglCreatePixmapSurface()。
WindowSurface:是和窗口相關的,也就是在屏幕閃給的一塊顯示區的封裝,渲染後即顯示在界面上。 PbufferSurface:在顯存中開闢一個控件,將渲染後的數據(幀)存放在這裏 pixmapSurface:以位圖的形式存放在內存中(支持性不好)
做離屏渲染我們可以選擇PbufferSurface或者PixmapSurface(其實WindowSurface也是可以的)
(幀緩存對象FBO是對幀緩存的封裝,性能要優於Pbuffer但並非可以完全替代Pbuffer)。
OpenGL操作的最終目標實際上是幀緩存(Frame Buffer)後面的各種表現形式則是EGL對Frame Buffer的封裝
對之前的代碼進行整理:
新建GLSurface類,對EGLSurface進行封裝 將GLRenderer類改爲抽象類,繼承於Thread GLRenderer添加一個阻塞隊列(消息隊列),用於交互和解耦 GLRenderer添加一個Event內部類 GLRenderer中添加生命週期,將渲染之類的具體工作放到實現類中 添加ShaderUtil類,將着色器創建的代碼封裝到這個類中
代碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 |
|
離屏渲染關鍵部分就是在makeOutputSurface()方法中的GLSurface.TYPE_PBUFFER_SURFACE這個case裏面
其實// 交換顯存(將surface顯存和顯示器的顯存交換)
EGL14.eglSwapBuffers(eglDisplay, output.eglSurface);對於離屏渲染是沒有用的,因爲本來就沒有顯示屏幕,於是我們可以這樣:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 |
|
在activity中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
|
我們可以看到我們其實進行了兩次渲染,第一次是離屏,第二次使用的是window_surface。爲了看到離屏的效果,我們必須從OpenGL的線程中取出像素數據,然後轉換成Bitmap設置給ImageView。
然後就能看到兩個三角形了,頂部的小三角形就是在後臺渲染的圖像了
知識補充
Display
EGL提供了圖形API如OpenGL ES和原生窗口系統如Linux 的X Window之間的一個結合層次,在EGL能夠確定可用的Surface類型之前,它必須打開和窗口系統的通信渠道,因爲是跨平臺的,每個窗口系統都有不同的遺言,所以EGL提供基本的不透明類型的EGLDisplai,該類型封裝了所有系統的相關屬性,用於原生窗口的系統接口,不同的窗口系統定義了不同的DIsplay屬性,最終都會Native化。任何使用EGL的應用程序必須執行的第一個操作是創建與初始化與本地EGL Display的連接:
1 2 |
|
eglGetDisplay用於獲取EGL DIsplay連接,display_id指定需要連接的Display,一般取默認值EGL_DEFAULT_DISPLAY
eglInitialize用於對Display進行初始化,初始化已經初始化了Display對這個Display沒有影響,major和minor獲取當前的EGL版本號
#配置Surface
EGL初始化Display完成後,就可以對Surface進行配置了,一種是查詢每個Surface配置,找出最好的選擇,另一種是指定一組需求,讓EGL推薦最佳匹配,兩者都返回一個EGLConfig,包括Surface相關的所有屬性。
1 2 3 4 5 6 7 |
|
eglGetConfigs用於獲取Display的frame buffer配置列表,dpy爲對應的Display,configs返回配置列表(可以爲NULL而只是通過num_config獲取配置列表可用的配置條目),size指定配置列表的大小(size大於1時configs需要有足夠的存儲空間),num_config返回配置列表獲取的配置條目。
eglGetConfigAttrib用於獲取EGL的frame buffer配置列表中某個具體屬性的值,dpy爲對應的Display,config爲待查詢的配置列表,attribute爲待查詢的具體屬性,value返回查詢的屬性值,
eglChooseConfig用於獲取Display的frame buffer配置列表,不過這個配置列表要與指定的屬性列表attrib_list匹配,dpy爲對應的Display,attrib_list爲config需要匹配的屬性列表,configs返回配置列表(非NULL時,size爲最大值,num_configs爲實際值,爲NULL時,忽略size),size指定配置列表的大小(size大於1時configs需要有足夠的存儲空間),num_config返回配置列表獲取的配置條目
創建Surface
Window Surface:可以理解爲EGL窗口,是屏幕上的渲染區域,有eglCreateWindowSurface創建
1 2 3 |
|
eglCreateWindowSurface用於創建Window Surface,dpy爲對應的EGL Display連接,config爲EGL frame buffer配置,定義了可用於Surface的frame buffer資源,win爲Native Window,是個平臺相關的類型,attrib_list爲Window Surface屬性列表,可以爲NULL。
1 |
|
用於銷燬surface,dpy爲對應的Display,surface爲將要銷燬的Surface,如果任何其它線程都沒有使用這個Surface時,Surface將被立即銷燬,否則要等到這個Surface不被任何線程使用時才銷燬,另外,對於一個PBuffer Surface來說,其資源要等到綁定到紋理對象的顏色緩衝區釋放後才被銷燬。
1 2 |
|
於獲取Surface信息,dpy爲對應的Display,surface待查詢的Surface,attribute爲待查詢的Surface屬性,value用於返回Surface屬性值
PBuffer Surface:稱作PBuffer(像素緩衝區Pixel Buffer的縮寫)的不可見屏幕外表面,和窗口一樣,PBuffer可以利用OpenGL ES 3.0中的任何硬件加速,PBuffer最常用於生成紋理貼圖,如果想要做的是渲染到一個紋理,那麼建議使用幀緩衝區對象(FBO)代替PBuffer,因爲幀緩衝區更高效,不過在某些FBO無法使用的情況下,PBuffer仍然有用,
1 2 |
|
eglCreatePbufferSurface用於創建off-screen的pixel buffer Surface,dpy爲對應的Display,config爲frame buffer配置,attrib_list爲PBuffer屬性列表,可以爲NULL
Pixmap Surface:
1 2 3 |
|
用於創建off-screen的Pixmap Surface,dpy爲對應的Display,config爲frame buffer配置,pixmap爲Native Pixmap,attrib_list爲Pixmap屬性列表,可以爲NULL
三種Surface的區別
window是on-screen的,
pbuffer和pixmap是off-screen的, window綁定到了NativeWindow
,pixmap綁定到了NativePixmap,
pbuffer沒有任何本地綁定 window是雙緩衝區的
pbuffer和pixmap是單緩衝區的, window默認在back buffer渲染,通過eglSwapBuffers交換到屏幕上顯示,
pbuffer在顯存中分配,由EGL_HEIGHT和EGL_WIDTH指定大小,常用作紋理數據,
pixmap綁定到本地的像素緩衝區,這個緩衝區可以被其它API使用。 創建不同的EGLSurface時,需要在EGLConfig中配置EGL_SURFACE_TYPE,window、pbuffer、pixmap分別對應於EGL_WINDOW_BIT、EGL_PBUFFER_BIT、EGL_PIXMAP_BUFFER。
1 |
|
對於Window Surface或back buffer來說,還需要通過eglSwapBuffers把off-sreen的back buffer交換到screen buffer,也就是說把EGL Surface的顏色緩衝區post到Native Window,內部調用了渲染API的Flush命令,返回EGL_FALSE時可能的原因爲EGL_BAD_DISPLAY、EGL_NOT_INITIALIZED、EGL_BAD_SURFACE、EGL_CONTEXT_LOST。
eglSwapBuffers對PBuffer Surface和Pixmap Surface無效。
創建Context
1 2 3 |
|
eglCreateContext用於創建EGL渲染Context,dpy爲對應的Display,config爲frame buffer配置,share_context爲其它的共享Context,可以設置爲EGL_NO_CONTEXT,attrib_list爲Context屬性列表,成功時返回新創建的EGLContext,失敗時返回EGL_NO_CONTEXT
ttrib_list屬性目前只有EGL_CONTEXT_CLIENT_VERSION,整數值,指定OpenGL ES版本,默認值爲1,還可以是2、3等其它版本值,創建OpenGL ES Context時設置這個屬性,也就是說渲染API爲EGL_OPENGL_ES_API時才設置這個屬性
1 2 |
|
創建了Surface和Context之後,因爲可能有多個Surface和Context,所以需要通過eglMakeCurrent綁定Context到Surface,dpy爲對應的Display,draw用於繪製,read用於讀,ctx爲要綁定的Context