這篇文章主要學(xué)習(xí),如何用OpenGL ES來(lái)渲染一張圖片桐磁。
準(zhǔn)備資料
OpenGL(Open Graphics Library) 是 Khronos Group (一個(gè)圖形軟硬件行業(yè)協(xié)會(huì),該協(xié)會(huì)主要關(guān)注圖形和多媒體方面的開(kāi)放標(biāo)準(zhǔn))開(kāi)發(fā)維護(hù)的一個(gè)規(guī)范竞端,它是硬件無(wú)關(guān)的铣焊。它主要為我們定義了用來(lái)操作圖形和圖片的一系列函數(shù)的 API拢蛋,OpenGL 本身并非 API楞遏。
OpenGL ES(OpenGL for Embedded Systems) 是 OpenGL 的子集,針對(duì)手機(jī)塌碌、PDA 和游戲主機(jī)等嵌入式設(shè)備而設(shè)計(jì)渊胸。該規(guī)范也是由 Khronos Group 開(kāi)發(fā)維護(hù)旬盯。
OpenGL ES 去除了四邊形(GL_QUADS)台妆、多邊形(GL_POLYGONS) 等復(fù)雜圖元,以及許多非絕對(duì)必要的特性胖翰,剩下最核心有用的部分接剩。可以理解成是一個(gè)在移動(dòng)平臺(tái)上能夠支持 OpenGL 最基本功能的精簡(jiǎn)規(guī)范萨咳。
一懊缺、基本概念
1. 緩存
OpenGL ES一部分運(yùn)行在CPU上, 一部分運(yùn)行在GPU上, 為了協(xié)調(diào)這兩部分的數(shù)據(jù)交換, 定義了Buffers(緩存)的概念. CPU和GPU都有獨(dú)自控制的內(nèi)存區(qū)域, 緩存可以避免數(shù)據(jù)在這兩塊區(qū)域之間進(jìn)行復(fù)制, 提高效率
2. 紋理、渲染和紋理渲染
紋理是一個(gè)用來(lái)保存圖像顏色的元素值的緩存,
渲染是指將數(shù)據(jù)生成圖像的過(guò)程;
紋理渲染就是將保存在內(nèi)存中的顏色值等數(shù)據(jù), 生成圖像的過(guò)程
3. 坐標(biāo)系
3.1 OpenGL ES中的坐標(biāo)系
OpenGL ES中的坐標(biāo)系是一個(gè)三維坐標(biāo)系, 用x,y,z來(lái)表示, 坐標(biāo)系的范圍是-1~1.
z軸的正方向指向屏幕外. 如果不考慮z軸, 左下角坐標(biāo)應(yīng)該為(-1, -1, 0), 右上角坐標(biāo)為(1,1,0).
3.2 紋理坐標(biāo)系
紋理坐標(biāo)系是一個(gè)二維坐標(biāo)系,用s,t軸來(lái)表示, t軸為數(shù)軸., s軸為橫軸 范圍是0~1.
在坐標(biāo)系中, 點(diǎn)的橫坐標(biāo)一般用u來(lái)表示, 點(diǎn)的縱坐標(biāo)一般用v來(lái)表示. 左下角坐標(biāo)為(0, 0), 右上角坐標(biāo)為(1, 1). 在UIKit中, 左下角為(0, 1), 右上角為(1. 0)
4. 紋理的相關(guān)概念
紋理元素(Texel) : 簡(jiǎn)稱紋素. 當(dāng)一個(gè)圖像初始化為一個(gè)紋理緩存后, 每個(gè)像素會(huì)變成一個(gè)紋素, 紋理的坐標(biāo)范圍是0~1, 在這個(gè)單位長(zhǎng)度內(nèi), 可能包含任意多個(gè)紋素.
片段(Fragment) : 視口坐標(biāo)中的顏色像素. 沒(méi)有使用紋理時(shí), 會(huì)使用對(duì)象頂點(diǎn)來(lái)計(jì)算片段的顏色; 使用紋理時(shí), 會(huì)根據(jù)紋素來(lái)計(jì)算.
光柵化(Rasterizing) : 將幾何形狀數(shù)據(jù)轉(zhuǎn)換為片段的步驟.
映射(Mapping) : 對(duì)齊頂點(diǎn)和紋素的方式. 即將頂點(diǎn)坐標(biāo)(x,y,z)和紋理坐標(biāo)(u,v)對(duì)應(yīng)起來(lái).
取樣(Sampling) : 在頂點(diǎn)固定后, 每個(gè)片段根據(jù)計(jì)算出來(lái)的(u, v)坐標(biāo), 去找相應(yīng)的紋素的過(guò)程.
幀緩存(Frame Buffer) : 一個(gè)接收渲染結(jié)果的緩沖區(qū), 為GPU指定存儲(chǔ)渲染結(jié)果的區(qū)域.
5. 緩存的使用
在實(shí)際應(yīng)用中, 我們需要用到各種緩存. 例如, 在紋理渲染之前, 會(huì)將圖像的數(shù)據(jù)初始化為一塊紋理緩存. 這塊緩存保存了圖像的數(shù)據(jù).
使用緩存可分為以下步驟:
-
生成(Generate) : 生成緩存標(biāo)識(shí)符, 函數(shù):
glGenBuffers()
.
-
生成(Generate) : 生成緩存標(biāo)識(shí)符, 函數(shù):
-
綁定(Bind) : 為接下來(lái)的操作, 綁定一個(gè)緩存. 函數(shù):
glBindBuffer()
.
-
綁定(Bind) : 為接下來(lái)的操作, 綁定一個(gè)緩存. 函數(shù):
-
緩存數(shù)據(jù)(Buffer Data): 從CPU的內(nèi)存復(fù)制數(shù)據(jù)到緩存的內(nèi)存中. 函數(shù):
glBufferData()
/glBufferSubData()
.
-
緩存數(shù)據(jù)(Buffer Data): 從CPU的內(nèi)存復(fù)制數(shù)據(jù)到緩存的內(nèi)存中. 函數(shù):
-
啟用(Enable) / 禁用(Disable) : 設(shè)置在接下來(lái)的渲染中是否使用緩存的數(shù)據(jù). 函數(shù):
glEnableVertexAttribArray()
/glDisableVertexAttribArray()
.
-
啟用(Enable) / 禁用(Disable) : 設(shè)置在接下來(lái)的渲染中是否使用緩存的數(shù)據(jù). 函數(shù):
-
設(shè)置指針(Set Pointers) : 告知緩存的數(shù)據(jù)類型, 以及相應(yīng)數(shù)據(jù)的偏移量. 函數(shù):
glVertexAttribPointer()
.
-
設(shè)置指針(Set Pointers) : 告知緩存的數(shù)據(jù)類型, 以及相應(yīng)數(shù)據(jù)的偏移量. 函數(shù):
-
繪圖(Draw) : 使用緩存的數(shù)據(jù)進(jìn)行繪制. 函數(shù):
glDrawArrays()
/glDrawElements()
.
-
繪圖(Draw) : 使用緩存的數(shù)據(jù)進(jìn)行繪制. 函數(shù):
-
刪除(Delete) : 清空緩存, 釋放內(nèi)存. 函數(shù):
glDeleteBuffers()
.
-
刪除(Delete) : 清空緩存, 釋放內(nèi)存. 函數(shù):
6. OpenGL ES上下文
OpenGL ES 是一個(gè)狀態(tài)機(jī), 相關(guān)的配置信息會(huì)保存在一個(gè)上下文(context)當(dāng)中. 這些值會(huì)被一直保存, 直到被修改.
我們也可以配置多個(gè)context, 通過(guò)調(diào)用[EAGLContext setCurrentContext:context]
來(lái)進(jìn)行切換當(dāng)前context.
7. OpenGL ES 中的圖元
圖元(Primitive) 是指 OpenGL ES 中支持渲染的基本圖形. OpenGL ES 只支持三種基本圖形: 頂點(diǎn), 線段和三角形. 復(fù)雜的圖形需要通過(guò)渲染多個(gè)三角形來(lái)實(shí)現(xiàn).
8. 如何去渲染三角形?
渲染流程如上圖. 在流程中, 頂點(diǎn)著色器和片段著色器都是可編程的部分. 著色器(Shader) 也是一個(gè)程序, 它們運(yùn)行在GPU上, 在主程序運(yùn)行的時(shí)候, 進(jìn)行動(dòng)態(tài)編,不用寫(xiě)死在代碼中. 編寫(xiě)著色器的語(yǔ)言是GLSL(OpenGL Shading Language) .
下面具體分析一下, 途中流程每一步都做了什么:
8.1 頂點(diǎn)數(shù)據(jù)
為了渲染一個(gè)三角形, 我們需要傳入一個(gè)包含3個(gè)三維頂點(diǎn)坐標(biāo)的數(shù)組.
每個(gè)頂點(diǎn)都有對(duì)應(yīng)的頂點(diǎn)屬性, 頂點(diǎn)屬性中可以包含任何我們想要的數(shù)據(jù).
為了讓OpenGL ES知道我們需要繪制的是一個(gè)三角形, 而不是頂點(diǎn)或者線段, 我們需要在調(diào)用繪制(Draw)指令的時(shí)候, 將圖元信息傳遞給OpenGL ES
8.2 頂點(diǎn)著色器
頂點(diǎn)著色器是運(yùn)行在GPU上的一個(gè)程序, 它會(huì)對(duì)每個(gè)頂點(diǎn)執(zhí)行一次運(yùn)算. 它可以使用頂點(diǎn)數(shù)據(jù)來(lái)計(jì)算該頂點(diǎn)的顏色, 光照, 紋理, 左邊等
頂點(diǎn)著色器的一個(gè)重要任務(wù)是進(jìn)行坐標(biāo)轉(zhuǎn)換. 例如將模型的原始坐標(biāo)系(一般為三維坐標(biāo)系)轉(zhuǎn)換為屏幕坐標(biāo)系
8.3 形狀(圖元)裝配
在頂點(diǎn)著色器輸出頂點(diǎn)坐標(biāo)后, 各個(gè)頂點(diǎn)按照繪制命令中的圖元信息參數(shù), 以及頂點(diǎn)索引數(shù)組, 被組裝成一個(gè)個(gè)圖元
通過(guò)這一步, 模型中的3D圖元已經(jīng)被轉(zhuǎn)換成屏幕上的2D圖元.
8.4 幾何著色器
在OpenGL的版本中, 頂點(diǎn)著色器和片段著色器中間, 還有一個(gè)可選的著色器, 叫做幾何著色器(Geometry Shader).
幾何著色器將圖元形式的一系列頂點(diǎn)的集合作為輸入, 它可以通過(guò)產(chǎn)生新的頂點(diǎn)來(lái)構(gòu)造出新的圖元, 從而生成其他的形狀.
OpenGL ES 目前不支持幾何著色器.
8.5 光柵化
在光柵化階段, 基本圖元被轉(zhuǎn)換成供片段著色器使用的片段.
片段表示可以被渲染到屏幕上的像素, 它包含位置, 顏色值, 紋理坐標(biāo)等信息. 這些是由圖元的頂點(diǎn)信息進(jìn)行插值計(jì)算得到的.
在片段著色器運(yùn)行之前會(huì)進(jìn)行裁剪, 處于視圖以外的部分會(huì)被裁剪掉, 用來(lái)提升執(zhí)行效率.
8.6 片段著色器
片段著色器的主要作用是計(jì)算每一個(gè)片段最終的顏色值(或者是否被丟棄), 片段著色器決定了最終屏幕上每一個(gè)像素點(diǎn)的顏色值.
8.7 測(cè)試與混合
在這一步, OpenGL ES 會(huì)根據(jù)片段是否被遮擋, 視圖上是否已存在繪制好的片段等情況, 對(duì)片段進(jìn)行混合或者丟棄.
最終保留下來(lái)的片段會(huì)被寫(xiě)入幀緩存(Frame Buffer), 呈現(xiàn)在屏幕上.
9. 如何渲染多邊形?
如圖所示培他,一個(gè)五邊形. 由于OpenGL ES 只能渲染三角形, 所以我們可以把它拆分成 3 個(gè)三角形來(lái)渲染鹃两。
渲染一個(gè)三角形,我們需要一個(gè)保存 3 個(gè)頂點(diǎn)的數(shù)組舀凛。這意味著我們渲染一個(gè)五邊形俊扳,需要用 9 個(gè)頂點(diǎn)。而且我們可以看到猛遍,其中 V0 馋记、 V2 、V3 都是重復(fù)的頂點(diǎn)懊烤,顯得有點(diǎn)冗余梯醒。
那么我們想復(fù)用這些重復(fù)的頂點(diǎn), OpenGL ES 有沒(méi)有提供方法呢?
有的.
OpenGL ES中, 對(duì)于三角形有三種渲染方式, 在給定的頂點(diǎn)數(shù)組相同的情況下, 我們可以指定我們想要的連接方式, 如下圖所示:
9.1 GL_TRIANGLES
GL_TRIANGLES
就是沒(méi)有復(fù)用頂點(diǎn)的方式, 以每個(gè)頂點(diǎn)繪制一個(gè)三角形, 第一個(gè)三角形使用 V0 、 V1 腌紧、V2 茸习,第二個(gè)使用 V3 、 V4 壁肋、V5 号胚,以此類推. 如果頂點(diǎn)的個(gè)數(shù)不是 3 的倍數(shù),那么最后的 1 個(gè)或者 2 個(gè)頂點(diǎn)會(huì)被舍棄.
9.2 GL_TRIANGLES_STRIP
GL_TRIANGLES_STRIP
在繪制三角形的時(shí)候墩划,會(huì)復(fù)用前兩個(gè)頂點(diǎn)涕刚。第一個(gè)三角形依然使用 V0 、 V1 乙帮、V2 杜漠,第二個(gè)則會(huì)使用 V1 、 V2 、V3驾茴,以此類推盼樟。第 n 個(gè)會(huì)使用 V(n-1) 、 V(n) 锈至、V(n+1) .
9.3 GL_TRIANGLES_FAN
GL_TRIANGLES_FAN
在繪制三角形的時(shí)候晨缴,會(huì)復(fù)用第一個(gè)頂點(diǎn)和前一個(gè)頂點(diǎn)。第一個(gè)三角形依然使用 V0 峡捡、 V1 击碗、V2 ,第二個(gè)則會(huì)使用 V0 们拙、 V2 稍途、V3,以此類推. 第 n 個(gè)會(huì)使用 V0 砚婆、 V(n) 械拍、V(n+1) 。這種方式看上去像是在繞著 V0 畫(huà)扇形.