OpenGL基礎(chǔ)——EGL谷徙、紋理
EGL
OpenGL是跨平臺接口拒啰,面對不同平臺的差異,需要有一個介于平-臺設(shè)備(視窗系統(tǒng))與OpenGL之間的橋梁幫助OpenGL擺平不同的差異完慧,EGL則是其中的橋梁谋旦。EGL同樣是一套標(biāo)準(zhǔn)接口,由各大平臺設(shè)備廠商實現(xiàn)。
EGL提供以下機制
- 與設(shè)備的原生窗口系統(tǒng)通信
- 查詢繪圖表面的可用類型和配置
- 創(chuàng)建繪圖表面
- 管理紋理貼圖
EGL搭建
Android開發(fā)過程中常見的GLSurfaceView已經(jīng)在底層幫我們構(gòu)建好了EGL册着。如果涉及NDK開發(fā)拴孤,也可以在底層自己根據(jù)EGL標(biāo)準(zhǔn)接口進行環(huán)境構(gòu)建。
- Display(EGLDisplay) 是對實際顯示設(shè)備的抽象
- Surface(EGLSurface)是對用來存儲圖像的內(nèi)存區(qū)域
- FrameBuffer 的抽象指蚜,包括 Color Buffer乞巧, Stencil Buffer ,Depth Buffer
- Context (EGLContext) 存儲 OpenGL ES繪圖的一些狀態(tài)信息
搭建流程
EGL的環(huán)境搭建主要包含以下8個步驟摊鸡,可以參考圖示一起了解绽媒。
-
創(chuàng)建OpenGL ES與原生窗口系統(tǒng)的連接 :調(diào)用eglGetDisplay方法得到EGLDisplay
EGLDisplay eglGetDisplay(EGLNativeDisplayType displayId);
-
初始化EGL連接 :調(diào)用 eglInitialize 方法初始化
EGLBoolean eglInitialize(EGLDisplay display, // 要進行初始化的EGL連接,即上一部中的返回值 EGLint *majorVersion, // 主版本號 EGLint *minorVersion) // 次版本號
-
確定渲染表面的配置信息 :調(diào)用 eglChooseConfig 方法得到 EGLConfig
?方法一:查詢所有可行的配置(eglGetConfigs)免猾,再讓EGL從中找出最優(yōu)選擇(eglGetConfigAttrib)
?方法二:指定一組需求是辕,然后再讓EGL推薦(eglChooseChofig)最佳配置
-
創(chuàng)建渲染表面 :通過 EGLDisplay 和 EGLConfig ,調(diào)用 eglCreateWindowSurface 方法創(chuàng)建渲染表面猎提,得到 EGLSurface
EGLSurface eglCreateWindowSurface( EGLDisplay display, //對應(yīng)的EGL display連接 EGLConfig config, //EGL frame buffer配置获三,定義了可用于Surface的frame buffer資源 NativeWindowType native_window, //原生窗口 EGLint const * attrib_list); // attrib_list為Window Surface屬性列表,可以為NULL锨苏,成功時返回新創(chuàng)建的
-
創(chuàng)建渲染上下文 :通過 EGLDisplay 和 EGLConfig 疙教,調(diào)用 eglCreateContext 方法創(chuàng)建渲染上下文,得到 EGLContext
EGLAPI EGLContext EGLAPIENTRY eglCreateContext( EGLDisplay display, EGLConfig config, EGLContext share_context, const EGLint *attribList);
-
綁定上下文 :通過 eglMakeCurrent 方法將EGLSurface伞租、EGLContext贞谓、EGLDisplay 三者綁定,接下來就可以使用 OpenGL 進行繪制了
EGLAPI EGLBoolean EGLAPIENTRY eglMakeCurrent( EGLDisplay display, EGLSurface draw, EGLSurface read, EGLContext context);
交換緩沖 :當(dāng)用 OpenGL 繪制結(jié)束后葵诈,使用 eglSwapBuffers 方法交換前后緩沖裸弦,將繪制內(nèi)容顯示到屏幕上
釋放 EGL 環(huán)境 :繪制結(jié)束,不再需要使用 EGL 時作喘,取消 eglMakeCurrent 的綁定理疙,銷毀 EGLDisplay、EGLSurface泞坦、EGLContext窖贤。
紋理
什么是紋理
紋理是表示物體表面的一幅或幾幅二維圖形,也稱紋理貼圖(texture)贰锁。紋理在OpenGL中非常常見主之,除了頂點坐標(biāo)的確定,紋理繪制李根、紋理操作的內(nèi)容都十分龐大。
為什么使用紋理
假設(shè)我們只通過對頂點操作完成模型繪制几睛,那么我們就需要繪制盡可能多的頂點來使模型立體逼真房轿,頂點數(shù)量的增多無疑會使渲染管線的效率降低。因為只通過頂點操作,所以在給模型上色時則需要設(shè)定頂點的顏色囱持『唤樱可想而知,單純的顏色設(shè)置相較于紋理的大面積插值會丟失更多細節(jié)纷妆,模型表面無法像紋理一樣豐富盔几。而通過緩沖區(qū)渲染到紋理的方式也能極大程度的減少內(nèi)存拷貝帶來的性能耗時。
- 減少渲染管線負(fù)荷
- 模型表面更加豐富
- 方便高效
基本概念
-
紋理坐標(biāo)
通常用UV表示紋理的橫縱坐標(biāo)掩幢,紋理坐標(biāo)左下角為(0逊拍,0),右上角為(1际邻,1)
-
紋素
紋理對象通常是通過紋理圖片讀取到的芯丧,這個數(shù)據(jù)保存到一個二維數(shù)組中,這個數(shù)組中的元素成為紋素(texel)世曾,紋素包含顏色值和alpha值缨恒。要想獲取紋理對象中的紋素,需要使用紋理坐標(biāo)(texture coordinate)指定轮听。比如一個256*256大小的紋理對象骗露,要取紋理坐標(biāo)為(0.5,1)的紋素極為(128血巍,256)萧锉。
[圖片上傳失敗...(image-5862be-1719817222342)]
因為一個紋素的大小可能無法對應(yīng)屏幕上的一個像素,或大于或小于藻茂,當(dāng)一個紋素小于一個像素時稱為縮芯允睢;一個紋素大于一個像素時稱為放大辨赐。放大和縮小都則需要設(shè)置不同的插值過濾算法使紋理在屏幕上顯示達到需要的效果优俘。
紋理的放大和縮小濾波設(shè)置相關(guān)的控制選項,GL_LINEAR為線性濾波掀序,GL_NEAREST為最近鄰過濾濾波帆焕。
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
紋理繪制步驟
紋理繪制主要包含4個步驟
- 創(chuàng)建紋理對象
- 綁定紋理對象
- 紋理過濾
- 紋理圖像加載
glGenTextures(1, &brushPreview->brushPreviewTextureId);
glBindTexture(GL_TEXTURE_2D, brushPreview->brushPreviewTextureId);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, drawWidth, drawHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE,NULL);
紋理使用
FBO渲染到紋理
通過幀緩沖渲染到紋理的方式,可以替代數(shù)據(jù)拷貝帶來的耗時影響不恭。FBO渲染到紋理的方式也能幫助實現(xiàn)下一幀需要上一幀渲染結(jié)果等業(yè)務(wù)需求叶雹。
//創(chuàng)建一個2D紋理
glGenTextures(1, &m_FboTextureId);
glBindTexture(GL_TEXTURE_2D, m_FboTextureId);
//創(chuàng)建幀緩沖區(qū)對象
glGenFramebuffers(1, &m_FboId);
glBindFramebuffer(GL_FRAMEBUFFER, m_FboId);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
//使用紋理作為附件
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_FboTextureId, 0);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, drawWidth, drawHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE,NULL);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
LOGD("FBO incomplete!!!!!!");
}
glClear(GL_COLOR_BUFFER_BIT);
glBindTexture(GL_TEXTURE_2D, GL_NONE);
glBindFramebuffer(GL_FRAMEBUFFER, GL_NONE);
將紋理綁定到FBO后,普通渲染只要使用上述綁定的紋理即可顯示離屏渲染的繪制結(jié)果换吧。
// 離屏渲染
glPixelStorei(GL_UNPACK_ALIGNMENT,1);
glViewport(0, 0, m_RenderImage.width, m_RenderImage.height);
glBindFramebuffer(GL_FRAMEBUFFER, m_FboId);
glUseProgram(m_FboProgramObj);
glBindVertexArray(m_VaoIds[1]);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, m_ImageTextureId);
glUniform1i(m_FboSamplerLoc, 0);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, (const void *)0);
glBindVertexArray(0);
glBindTexture(GL_TEXTURE_2D, 0);
// 普通渲染
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glViewport(0, 0, screenW, screenH);
glUseProgram(m_ProgramObj);
glBindVertexArray(m_VaoIds[0]);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, m_FboTextureId);
glUniform1i(m_SamplerLoc, 0);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, (const void *)0);
glBindTexture(GL_TEXTURE_2D, GL_NONE);
glBindVertexArray(GL_NONE);
讀取顏色緩沖區(qū)
void glReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels)
紋理映射
紋理映射可以簡單理解為通過不同的矩陣變換轉(zhuǎn)換紋理的坐標(biāo)和像素折晦,從而展現(xiàn)不同的紋理結(jié)果。紋理映射主要體現(xiàn)在平移沾瓦、旋轉(zhuǎn)满着、縮放等谦炒。
理映射部分使用場景
- 大尺寸紋理需要裁剪出其中的一部分作為紋理進行渲染
- 紋理疊加時顏色混合處理
- 紋理尺寸與窗口尺寸不匹配時的簡單映射
- 紋理像素過大但需要在不同小尺寸下進行渲染
總結(jié)
本部分內(nèi)容的介紹能夠幫助我們了解EGL和紋理基本內(nèi)容。EGL環(huán)境的搭建在實際開發(fā)過程中使用較少风喇,基本一勞永逸宁改,但紋理的使用則是OpenGL開發(fā)過程中隨處可見、使用量巨大魂莫。紋理的操作關(guān)系渲染管線的最終呈現(xiàn)还蹲,對紋理的相關(guān)操作可以在片段著色器中實現(xiàn)更多不同有趣的效果。