OpenGL基礎(chǔ)——EGL、紋理

OpenGL基礎(chǔ)——EGL谷徙、紋理

EGL

OpenGL是跨平臺接口拒啰,面對不同平臺的差異,需要有一個介于平-臺設(shè)備(視窗系統(tǒng))與OpenGL之間的橋梁幫助OpenGL擺平不同的差異完慧,EGL則是其中的橋梁谋旦。EGL同樣是一套標(biāo)準(zhǔn)接口,由各大平臺設(shè)備廠商實現(xiàn)。

EGL-1.png

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)建。

EGL中的相關(guān)參數(shù).jpg
  • 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個步驟摊鸡,可以參考圖示一起了解绽媒。

  1. 創(chuàng)建OpenGL ES與原生窗口系統(tǒng)的連接 :調(diào)用eglGetDisplay方法得到EGLDisplay

    EGLDisplay eglGetDisplay(EGLNativeDisplayType displayId);
    
  2. 初始化EGL連接 :調(diào)用 eglInitialize 方法初始化

    EGLBoolean eglInitialize(EGLDisplay display,   // 要進行初始化的EGL連接,即上一部中的返回值
                  EGLint *majorVersion, // 主版本號
                  EGLint *minorVersion) // 次版本號
    
  3. 確定渲染表面的配置信息 :調(diào)用 eglChooseConfig 方法得到 EGLConfig

    ?方法一:查詢所有可行的配置(eglGetConfigs)免猾,再讓EGL從中找出最優(yōu)選擇(eglGetConfigAttrib)

    ?方法二:指定一組需求是辕,然后再讓EGL推薦(eglChooseChofig)最佳配置

  4. 創(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)建的
    
  5. 創(chuàng)建渲染上下文 :通過 EGLDisplay 和 EGLConfig 疙教,調(diào)用 eglCreateContext 方法創(chuàng)建渲染上下文,得到 EGLContext

    EGLAPI EGLContext EGLAPIENTRY eglCreateContext(
            EGLDisplay display, EGLConfig config,
                        EGLContext share_context,
                        const EGLint *attribList);
    
  6. 綁定上下文 :通過 eglMakeCurrent 方法將EGLSurface伞租、EGLContext贞谓、EGLDisplay 三者綁定,接下來就可以使用 OpenGL 進行繪制了

    EGLAPI EGLBoolean EGLAPIENTRY eglMakeCurrent(
            EGLDisplay display,                     
            EGLSurface draw,
                          EGLSurface read, EGLContext context);
    
  7. 交換緩沖 :當(dāng)用 OpenGL 繪制結(jié)束后葵诈,使用 eglSwapBuffers 方法交換前后緩沖裸弦,將繪制內(nèi)容顯示到屏幕上

  8. 釋放 EGL 環(huán)境 :繪制結(jié)束,不再需要使用 EGL 時作喘,取消 eglMakeCurrent 的綁定理疙,銷毀 EGLDisplay、EGLSurface泞坦、EGLContext窖贤。

搭建EGL環(huán)境的流程.png

紋理

什么是紋理

紋理是表示物體表面的一幅或幾幅二維圖形,也稱紋理貼圖(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)

紋理坐標(biāo).png
  • 紋素

    紋理對象通常是通過紋理圖片讀取到的芯丧,這個數(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è)置不同的插值過濾算法使紋理在屏幕上顯示達到需要的效果优俘。

紋理坐標(biāo)轉(zhuǎn)換.png

紋理的放大和縮小濾波設(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個步驟

  1. 創(chuàng)建紋理對象
  2. 綁定紋理對象
  3. 紋理過濾
  4. 紋理圖像加載
    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)更多不同有趣的效果。

附錄

OpenGL ES 著色器與程序解析

OpenGL ES 2D紋理載入和處理

OpenGL基礎(chǔ)——渲染管線

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末耙考,一起剝皮案震驚了整個濱河市谜喊,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌琳骡,老刑警劉巖锅论,帶你破解...
    沈念sama閱讀 216,651評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異楣号,居然都是意外死亡最易,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評論 3 392
  • 文/潘曉璐 我一進店門炫狱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來藻懒,“玉大人,你說我怎么就攤上這事视译℃揖#” “怎么了?”我有些...
    開封第一講書人閱讀 162,931評論 0 353
  • 文/不壞的土叔 我叫張陵酷含,是天一觀的道長鄙早。 經(jīng)常有香客問我,道長椅亚,這世上最難降的妖魔是什么限番? 我笑而不...
    開封第一講書人閱讀 58,218評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮呀舔,結(jié)果婚禮上弥虐,老公的妹妹穿的比我還像新娘。我一直安慰自己媚赖,他們只是感情好霜瘪,可當(dāng)我...
    茶點故事閱讀 67,234評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著惧磺,像睡著了一般颖对。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上磨隘,一...
    開封第一講書人閱讀 51,198評論 1 299
  • 那天惜互,我揣著相機與錄音布讹,去河邊找鬼。 笑死训堆,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的白嘁。 我是一名探鬼主播坑鱼,決...
    沈念sama閱讀 40,084評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼絮缅!你這毒婦竟也來了鲁沥?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,926評論 0 274
  • 序言:老撾萬榮一對情侶失蹤耕魄,失蹤者是張志新(化名)和其女友劉穎画恰,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體吸奴,經(jīng)...
    沈念sama閱讀 45,341評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡允扇,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,563評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了则奥。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片考润。...
    茶點故事閱讀 39,731評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖读处,靈堂內(nèi)的尸體忽然破棺而出糊治,到底是詐尸還是另有隱情,我是刑警寧澤罚舱,帶...
    沈念sama閱讀 35,430評論 5 343
  • 正文 年R本政府宣布井辜,位于F島的核電站,受9級特大地震影響管闷,放射性物質(zhì)發(fā)生泄漏粥脚。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,036評論 3 326
  • 文/蒙蒙 一渐北、第九天 我趴在偏房一處隱蔽的房頂上張望阿逃。 院中可真熱鬧,春花似錦赃蛛、人聲如沸恃锉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,676評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽破托。三九已至,卻和暖如春歧蒋,著一層夾襖步出監(jiān)牢的瞬間土砂,已是汗流浹背州既。 一陣腳步聲響...
    開封第一講書人閱讀 32,829評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留萝映,地道東北人吴叶。 一個月前我還...
    沈念sama閱讀 47,743評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像序臂,于是被迫代替她去往敵國和親蚌卤。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,629評論 2 354

推薦閱讀更多精彩內(nèi)容