NO.13 - OpenGL ES及GLKit

OpenGL ES

  • OpenGL ES是OpenGL的子集
  • 是針對(duì)嵌入式設(shè)備及移動(dòng)終端設(shè)備的高級(jí)3D圖形應(yīng)用程序,例如iOS、Android、Windows等
  • OpenGL ES 是跨平臺(tái)的,不會(huì)提供窗口相關(guān)方法,需要系統(tǒng)各自提供載體
OpenGL ES渲染流程
OpenGL ES渲染流程

OpenGL ES的渲染主要分為兩部分:CPU和GPU

CPU部分

  • app代碼通過OpenGL ES API,會(huì)調(diào)度OpenGL ES Framework
  • 通過OpenGL ES client 調(diào)度 OpenGL ES server,將頂點(diǎn)數(shù)據(jù)等傳遞到GPU

GPU部分

  • 做一些圖形硬件的處理,例如光柵化、顯示等
OpenGL ES 圖形管道
圖形管道
  • API獲得頂點(diǎn)數(shù)據(jù),將頂點(diǎn)數(shù)據(jù)從內(nèi)存中拷貝至頂點(diǎn)緩沖區(qū)(顯存)
  • 拿到數(shù)據(jù)之后,通過attribute通道傳遞至頂點(diǎn)著色器,同時(shí),紋理坐標(biāo)通過Texture通道傳遞到頂點(diǎn)著色器和片元著色器
  • 然后,圖元裝配,即圖元的連接方式,一共有9種,常用的有6種,此步驟將頂點(diǎn)變換為圖形
  • 光柵化:確定圖形與屏幕對(duì)應(yīng)的位置
  • 片元/片段/像素著色器:處理對(duì)應(yīng)像素點(diǎn)的顏色值
  • 在將處理好的每個(gè)像素點(diǎn)的顏色值存儲(chǔ)到幀緩存區(qū),然后在顯示器中顯示
  • API:可以通過API操作頂點(diǎn)緩沖區(qū)、頂點(diǎn)著色器、紋理坐標(biāo)、片段著色器

下面附上蘋果官方文檔圖示,原理都是一致的,只是描述方式不同。


蘋果官方圖示
  • App:提供圖元裝配頂點(diǎn)信息,圖片信息
  • Vertex(頂點(diǎn)著色器):處理頂點(diǎn) -- 圖形變換(旋轉(zhuǎn)、縮放、平移)
  • Geometry(圖元裝配):圖元裝配 + 裁剪(超出屏幕部分被裁剪)
  • Fragment(片元著色器):紋理處理 + 霧化處理
  • Framebuffer Operation(幀緩沖區(qū)):透明度混合、模板、深度測(cè)試;最后在混合,這些操作都是在即將顯示時(shí),在幀緩沖區(qū)中完成的動(dòng)作
頂點(diǎn)著色器 (vertex shader)
頂點(diǎn)著色器
  • 輸入,有3種方式
    • 通過attribute通道輸入頂點(diǎn)數(shù)據(jù),提供每個(gè)頂點(diǎn)的數(shù)據(jù)
    • 通過uniform通道輸入統(tǒng)一變量,即頂點(diǎn)/片元著色器中使用的不變的數(shù)據(jù)
    • 采樣器:表示頂點(diǎn)著色器使用紋理的特殊統(tǒng)一變量類型
  • 輸出:經(jīng)過處理的最終頂點(diǎn)數(shù)據(jù),有2種
    • gl_Position,是GLSL 的內(nèi)建變量,是將處理后的最終頂點(diǎn)數(shù)據(jù)賦值給它
    • gl_PointSize,是指點(diǎn)的尺寸,即可以在頂點(diǎn)著色器中修改每個(gè)點(diǎn)的大小,使用率較低

頂點(diǎn)著色器的業(yè)務(wù)內(nèi)容:
1.矩陣變換位置
2.計(jì)算光照公式?成逐頂點(diǎn)顏?
3.?成/變換紋理坐標(biāo)

總結(jié): 它可以?于執(zhí)行?自定義計(jì)算,實(shí)施新的變換,照明或者傳統(tǒng)的固定功能所不允許 的基于頂點(diǎn)的效果。

頂點(diǎn)著色器GLSL代碼示例:

attribute、uniform 表示client與server之間的通道
其中的vec4、vec2都是向量類型,表示四維向量和二維向量
mat4:4*4矩陣
varying是修飾符:通過varying將紋理坐標(biāo)傳入到片元著色器
lowp:低精讀
main中的操作
  實(shí)現(xiàn)了紋理坐標(biāo)的橋接
  實(shí)現(xiàn)了頂點(diǎn)旋轉(zhuǎn)矩陣的相乘:列向量 與 列矩陣 相乘,得到旋轉(zhuǎn)后的頂點(diǎn)坐標(biāo)
  將上述得到的頂點(diǎn)坐標(biāo),賦值給gl_Position
*********************************************************
attribute vec4 position;
attribute vec2 textCoordinate; 
uniform mat4 rotateMatrix; 
varying lowp vec2 varyTextCoord; 
void main()
{
    varyTextCoord = textCoordinate;
    vec4 vPos = position;
    vPos = vPos * rotateMatrix;
    gl_Position = vPos; 
}
圖元裝配

頂點(diǎn)著?器之后,下?個(gè)階段就是圖元裝配.
圖元(Primitive): 點(diǎn),線,三?角形等.
圖元裝配: 將頂點(diǎn)數(shù)據(jù)計(jì)算成?個(gè)個(gè)圖元.在這個(gè)階段會(huì)執(zhí)?裁剪、透視分割和 Viewport變換操作。
圖元類型和頂點(diǎn)索確定將被渲染的單獨(dú)圖元。對(duì)于每個(gè)單獨(dú)圖元及其對(duì)應(yīng)的頂點(diǎn),圖元裝配階段執(zhí)行的操作包括:將頂點(diǎn)著?器的輸出值執(zhí)?裁剪、透視分割、視?變換后進(jìn)?入光柵化階段。

光柵化

將圖元轉(zhuǎn)化為一組二維片段的過程,主要是由于屏幕是2D的,所以轉(zhuǎn)換的像素點(diǎn)也是二維的


光柵化
片源著色器(fragment shader)
片源著色器
  • 輸入同頂點(diǎn)著色器一樣,有3種方式
    • 由頂點(diǎn)著色器橋接傳遞過來的紋理坐標(biāo)等
    • 通過uniform通道輸入統(tǒng)一變量,即頂點(diǎn)/片元著色器中使用的不變的數(shù)據(jù)
    • 采樣器:表示頂點(diǎn)著色器使用紋理的特殊統(tǒng)一變量類型,例如紋理就是通過采樣器傳遞
  • 輸出:某個(gè)像素點(diǎn)經(jīng)過片元著色器處理后的結(jié)果。

片元著色器的業(yè)務(wù)內(nèi)容:
1.計(jì)算顏色
2.獲取紋理值
3.往像素點(diǎn)中填充顏色值(紋理值/顏色值)

總結(jié): 它可以?于圖片/視頻/圖形中每個(gè)像素的顏?填充(?如給視頻添加濾鏡,實(shí)際上就是將視頻中每個(gè)圖?的像素點(diǎn)顏?填充進(jìn)行修改.)

片元著色器GLSL代碼示例

varying:必須和頂點(diǎn)著色器中一模一樣,這樣才能傳遞紋理坐標(biāo)
sampler2D 采樣器類型
texture2D(紋理采樣器,紋理坐標(biāo)):獲取對(duì)應(yīng)位置/坐標(biāo)的顏色值,簡稱獲得紋素
gl_FragColor(內(nèi)建變量):將最終的顏色值賦值給它
*********************************************************
varying lowp vec2 varyTextCoord; 
uniform sampler2D colorMap;
void main() {
gl_FragColor = texture2D(colorMap, varyTextCoord); }
逐片段操作

這個(gè)過程都是GPU內(nèi)部處理的,開發(fā)者并不需要關(guān)心,將處理好的數(shù)據(jù)存儲(chǔ)到幀緩存區(qū),最后讀取幀緩存區(qū)將圖形顯示到屏幕上

逐片段操作
  • 像素歸屬測(cè)試: 確定幀緩存區(qū)中位置(Xw,Yw)的像素?前是不是歸屬于OpenGL ES所 有. 例如,如果一個(gè)顯示OpenGL ES幀緩存區(qū)View被另外一個(gè)View 所遮蔽.則窗口系統(tǒng)可以確定被遮蔽的像素不屬于OpenGL ES上下文.從而不全顯示這些像素.而像素歸屬測(cè)試是OpenGL ES 的?部分,它不由開發(fā)者開人為控制,?是由OpenGL ES內(nèi)部進(jìn)?.
  • 裁剪測(cè)試: 裁剪測(cè)試確定(Xw,Yw)是否位于作為OpenGL ES狀態(tài)的?部分裁剪矩形范圍內(nèi).如果該?段位于裁剪區(qū)域之外,則被拋棄.
  • 深度測(cè)試: 輸?片段的深度值進(jìn)步?較,確定?段是否拒絕測(cè)試
  • 混合: 混合將新生成的?段顏色與保存在幀緩存的位置的顏色值組合起來.
  • 抖動(dòng): 抖動(dòng)可?于最?化因?yàn)槭?有限精度在幀緩存區(qū)中保存顏?值?產(chǎn)生的偽像.
OpenGL ES的應(yīng)用

圖片濾鏡

  • 獲取圖片中的每一個(gè)像素點(diǎn)
  • 像素點(diǎn)做飽和度處理
  • 得到新的顏色
  • 將新的顏色翻入幀緩存區(qū)
  • 最后進(jìn)行顯示

視頻濾鏡
原理以及處理方式是一樣的(GLSL代碼),視頻也是一幀一幀處理的,而一幀就是一張圖片

  • 獲得視頻MP4文件
  • 拿到h264(視頻壓縮文件) -
  • 將視頻解碼(解壓),還原成一幀一幀的圖片
  • 針對(duì)一幀一幀的圖片進(jìn)行處理

EGL(Embedded Graphics Library)

  • OpenGL ES 命令需要渲染上下?和繪制表面才能完成圖形圖像的繪制
    渲染上下?: 存儲(chǔ)相關(guān)OpenGL ES狀態(tài),是一個(gè)狀態(tài)機(jī)
  • 繪制表面:?于繪制圖元的表面,需要指定渲染的緩存區(qū),例如顏?緩、深度和模板
  • OpenGL ES API并沒有提供如何創(chuàng)建渲染上下文或者上下文如何連接到原生窗口系 統(tǒng). EGLKhronos 渲染API(如OpenGL ES) 和原?窗?系統(tǒng)之間的接?. 唯?支持 OpenGL ES 卻不支持EGL的平臺(tái)是iOS. Apple 提供?己的EGL API的iOS實(shí)現(xiàn),稱為EAGL
  • 因?yàn)槊總€(gè)窗?系統(tǒng)都有不同的定義,所以EGL提供基本的不透明類型—EGLDisplay, 這 個(gè)類型封裝了所有系統(tǒng)相關(guān)性,用于和原生窗?系統(tǒng)接?

GLKit

了解GLKit框架前,有必要先看下GLKit的蘋果官方文檔GLKitAPI

GLKit框架的設(shè)計(jì)目標(biāo)是為了簡化基于OpenGL / OpenGL ES的應(yīng)用開發(fā)。它的出現(xiàn)加快OpenGL ESOpenGL應(yīng)用程序開發(fā)。 使用數(shù)學(xué)庫,背景紋理加載,預(yù)先創(chuàng)建的著色器效果,以及標(biāo)準(zhǔn)視圖和視圖控制器來實(shí)現(xiàn)渲染循環(huán)。
GLKit框架提供了功能和類,可以減少創(chuàng)建新的基于著?器的應(yīng)用程序所需的?作量,或者?持依賴早期版本的OpenGL ESOpenGL提供的固定函數(shù)頂點(diǎn)或?段處理的現(xiàn)有應(yīng)用程序。

GLKit功能

  • 提供高性能的數(shù)學(xué)運(yùn)算(Math libraries):提供常用的向量,四元數(shù)和矩陣運(yùn)算。

  • 加載紋理(Texture loading):允許加載各種紋理,且可以后臺(tái)加載,通過GLKTextureLoader類來加載

  • 提供常見的著色器(effect):包含以下3種著色器

    • GLKBaseEffect
    • GLKReflectionMapEffect
    • GLKSkyboxEffect
  • 提供視圖視圖以及視圖控制器:GLKViewGLKViewController

    • GLKView:提供繪制場(chǎng)所,繼承自UIView
    • GLKViewController:?于繪制視圖內(nèi)容的管理與呈現(xiàn),繼承自UIViewController)

使用GLKit視圖呈現(xiàn)OpenGL ES 內(nèi)容
下圖來自Apple官方文檔Drawing with OpenGL ES and GLKit

GLKit呈現(xiàn)圖片過程

通過GLKit展示圖片,主要有以下三個(gè)步驟

  • GLKView的創(chuàng)建和配置
  • 使用GLKView對(duì)象繪制圖形,并存儲(chǔ)到幀緩存區(qū)
  • 從幀緩存區(qū)中讀取數(shù)據(jù),顯示到屏幕上

GLKit 常用API

GLKit文理加載
1.GLKTextureInfo 創(chuàng)建OpenGL紋理信息
@interface GLKTextureInfo : NSObject <NSCopying>
{
@private
    GLuint                      name;
    GLenum                      target;
    GLuint                      width;
    GLuint                      height;
    GLuint                      depth;
    GLKTextureInfoAlphaState    alphaState;
    GLKTextureInfoOrigin        textureOrigin;
    BOOL                        containsMipmaps;
    GLuint                      mimapLevelCount;
    GLuint                      arrayLength;
}

@property (readonly) GLuint                     name;
@property (readonly) GLenum                     target;
@property (readonly) GLuint                     width;
@property (readonly) GLuint                     height;
@property (readonly) GLuint                     depth;
@property (readonly) GLKTextureInfoAlphaState   alphaState;
@property (readonly) GLKTextureInfoOrigin       textureOrigin;
@property (readonly) BOOL                       containsMipmaps;
@property (readonly) GLuint                     mimapLevelCount;
@property (readonly) GLuint                     arrayLength;

@end

GLKTextureInfo對(duì)象里面有很多屬性,常用的如下:

name : OpenGL 上下文中紋理名稱
target: 紋理綁定的目標(biāo)
height : 加載的紋理?度
width : 加載紋理的寬度
textureOrigin: 加載紋理中的原點(diǎn)位置
alphaState: 加載紋理中alpha分量狀態(tài)
containsMipmaps: 布爾值,加載的紋理是否包含mip貼圖

2.GLTextureLoader簡化從各種資源文件中加載紋理

GLTextureLoader初始化方式:

- initWithSharegroup: 初始化一個(gè)新的紋理加載到對(duì)象中
- initWithShareContext:初始化一個(gè)新的紋理加載對(duì)象

常見的加載方式如下:

  • 從?件中加載紋理

+ textureWithContentsOfFile:options:errer:從?件加載2D紋理圖像并從數(shù)據(jù)中
創(chuàng)建新的紋理
- textureWithContentsOfFile:options:queue:completionHandler:從?件中異步
加載2D紋理圖像,并根據(jù)數(shù)據(jù)創(chuàng)建新紋理

  • 從URL加載紋理

- textureWithContentsOfURL:options:error:從URL加載2D紋理圖像并從數(shù)據(jù)創(chuàng)
建新紋理
- textureWithContentsOfURL:options:queue:completionHandler:從URL異步
加載2D紋理圖像,并根據(jù)數(shù)據(jù)創(chuàng)建新紋理.

  • 從內(nèi)存中表示創(chuàng)建紋理

+ textureWithContentsOfData:options:errer:從內(nèi)存空間加載2D紋理圖像,并根據(jù)數(shù)據(jù)創(chuàng)建新紋理
- textureWithContentsOfData:options:queue:completionHandler:從內(nèi)存空間異步加載2D紋理圖像,并從數(shù)據(jù)中創(chuàng)建新紋理

  • 從CGImages創(chuàng)建紋理

- textureWithCGImage:options:error:從Quartz圖像加載2D紋理圖像并從數(shù)據(jù)創(chuàng)建新紋理
- textureWithCGImage:options:queue:completionHandler:從Quartz圖像異步加載2D紋理理圖像,并根據(jù)數(shù)據(jù)創(chuàng)建新紋理

  • 從URL加載多維創(chuàng)建紋理

+ cabeMapWithContentsOfURL:options:errer:從單個(gè)URL加載?方體貼圖紋理圖像,并根據(jù)數(shù)據(jù)創(chuàng)建新紋理
- cabeMapWithContentsOfURL:options:queue:completionHandler:從單個(gè)
URL異步加載?方體貼圖紋理圖像,并根據(jù)數(shù)據(jù)創(chuàng)建新紋理

  • 從?件加載多維數(shù)據(jù)創(chuàng)建紋理

+ cubeMapWithContentsOfFile:options:errer:從單個(gè)文件加載?方體貼圖紋理對(duì)象,并從數(shù)據(jù)中創(chuàng)建新紋理理
- cubeMapWithContentsOfFile:options:queue:completionHandler:從單個(gè)?件異步加載?方體貼圖紋理對(duì)象,并從數(shù)據(jù)中創(chuàng)建新紋理
+ cubeMapWithContentsOfFiles:options:errer:從?系列文件中加載?方體貼圖紋理圖像,并從數(shù)據(jù)總創(chuàng)建新紋理
- cubeMapWithContentsOfFiles:options:options:queue:completionHandler:
從一系列文件異步加載?方體貼圖紋理圖像,并從數(shù)據(jù)中創(chuàng)建新紋理

GLKit OpenGL ES視圖渲染

GLKView使用OpenGL ES繪制內(nèi)容的視圖默認(rèn)實(shí)現(xiàn)

  • 初始化視圖
    - initWithFrame:context:初始化新視圖
    delegate 視圖的代理
  • 配置幀緩存區(qū)對(duì)象
    drawableColorFormat顏?渲染緩存區(qū)格式
    drawableDepthFormat深度渲染緩存區(qū)格式
    drawableStencilFormat 模板渲染緩存區(qū)的格式
    drawableMultisample多重采樣緩存區(qū)的格式
  • 幀緩存區(qū)屬性
    drawableHeight底層緩存區(qū)對(duì)象的?度(以像素為單位)
    drawableWidth 底層緩存區(qū)對(duì)象的寬度(以像素為單位)
  • 繪制視圖的內(nèi)容
    context繪制視圖內(nèi)容時(shí)使?用的OpenGL ES上下文
    - bindDrawable將底層FrameBuffer對(duì)象綁定到OpenGL ES
    enableSetNeedsDisplay 布爾值,指定視圖是否響應(yīng)使得視圖內(nèi)容?效的消息
  • display ?即重繪視圖內(nèi)容
    snapshot繪制視圖內(nèi)容并將其作為新圖像對(duì)象返回
  • 刪除視圖FrameBuffer對(duì)象
    - deleteDrawable刪除與視圖關(guān)聯(lián)的可繪制對(duì)象

GLKViewDelegate?于GLKView對(duì)象回調(diào)方法

繪制視圖的內(nèi)容
- glkView:drawInRect:繪制視圖內(nèi)容 (必須實(shí)現(xiàn)代理)

** GLKViewController管理OpenGL ES渲染循環(huán)的視圖控制器**

  • 更新
    - (void) glkViewControllerUpdate:
  • 配置幀速率
    preferredFramesPerSecond視圖控制器調(diào)?視圖以及更新視圖內(nèi)容的速率
    framesPerSencond 視圖控制器器調(diào)用視圖以及更新其內(nèi)容的實(shí)際速率
  • 配置GLKViewController代理
    delegate視圖控制器的代理
  • 控制幀更新
    paused 布爾值,渲染循環(huán)是否已暫停
    pausedOnWillResignActive布爾值,當(dāng)前程序重新激活動(dòng)狀態(tài)時(shí)視圖控制器是
    否?動(dòng)暫停渲染循環(huán)
    resumeOnDidBecomeActive布爾值,當(dāng)前程序變?yōu)榛顒?dòng)狀態(tài)時(shí)視圖控制是否?動(dòng)恢復(fù)呈現(xiàn)循環(huán)
  • 獲取有關(guān)View 更更新信息
    frameDisplayed視圖控制器?創(chuàng)建以來發(fā)送的幀更新數(shù)
    timeSinceFirstResume自視圖控制器第一次恢復(fù)發(fā)送更新事件以來經(jīng)過的時(shí)間量
    timeSinceLastResume自上次視圖控制器恢復(fù)發(fā)送更新事件以來更新的時(shí)間量
    timeSinceLastUpdate?上次視圖控制器調(diào)?委托?法以及經(jīng)過的時(shí)間量
    glkViewControllerUpdate:timeSinceLastDraw自上次視圖控制器調(diào)用視圖display方法以來經(jīng)過的時(shí)間量

GLKViewControllerDelegate渲染循環(huán)回調(diào)?法

處理更新事件
- glkViewControllerUpdate:在顯示每個(gè)幀之前調(diào)用
暫停/恢復(fù)通知
- glkViewController : willPause:在渲染循環(huán)暫停或恢復(fù)之前調(diào)?

GLKBaseEffect 一種簡單光照/著?系統(tǒng),?于基于著?器OpenGL渲染

  • 命名Effect
    labelEffect(效果)命名
  • 配置模型視圖轉(zhuǎn)換
    transform綁定效果時(shí)應(yīng)?于頂點(diǎn)數(shù)據(jù)的模型視圖,投影和紋理變換
  • 配置光照效果
    lightingType ?于計(jì)算每個(gè)?段的光照策略,GLKLightingType
  • GLKLightingType
    GLKLightingTypePerVertex表示在三?形中每個(gè)頂點(diǎn)執(zhí)行光照計(jì)算,然后在三?形進(jìn)?插值
    GLKLightingTypePerPixel表示光照計(jì)算的輸?入在三?形內(nèi)插?,并且在每個(gè)片段執(zhí)行光照計(jì)算
  • 配置光照
    lightModelTwoSided 布爾值,表示為基元的兩側(cè)計(jì)算光照
    material 計(jì)算渲染圖元光照使用的材質(zhì)屬性
    lightModelAmbientColor 環(huán)境顏?色應(yīng)用效果渲染的所有圖元.
    light0 場(chǎng)景中第一個(gè)光照屬性 light1 場(chǎng)景中第二個(gè)光照屬性 light2 場(chǎng)景中第三個(gè)光照屬性
  • 配置紋理
    texture2d0第?個(gè)紋理屬性 texture2d1第二個(gè)紋理屬性
    textureOrder紋理應(yīng)用于渲染圖元的順序
  • 配置霧化
    fog 應(yīng)?于場(chǎng)景的霧屬性
  • 配置顏?信息
    colorMaterialEnable 布爾值,表示計(jì)算光照與材質(zhì)交互時(shí)是否使?顏色頂點(diǎn)屬性
    useConstantColor 布爾值,指示是否使用常量顏色
    constantColor不提供每個(gè)頂點(diǎn)顏?數(shù)據(jù)時(shí)使?常量顏?
  • 準(zhǔn)備繪制效果
    - prepareToDraw準(zhǔn)備渲染效果

注意
1、GLKit中最多只有3個(gè)光照,2個(gè)紋理
2、常量顏色:黑色

GLKit加載圖片案例

首先實(shí)現(xiàn)一個(gè)修改viewbackgroundColor

創(chuàng)建一個(gè)iOS項(xiàng)目,并將系統(tǒng)創(chuàng)建的ViewController的父類由UIViewController修改為GLKViewController,其中的view的父類由UIView修改為GLKView

ViewController.h文件中導(dǎo)入GLKit框架的頭文件#import <GLKit/GLKit.h>
ViewController.h文件中導(dǎo)入OpenGL ES相關(guān)頭文件#import <OpenGLES/ES3/gl.h>#import <OpenGLES/ES3/glext.h>

  • 定義一個(gè)EAGLContext成員變量
#import <OpenGLES/ES3/gl.h>
#import <OpenGLES/ES3/glext.h>

@interface ViewController ()
{
    EAGLContext *context;
    
@end
  • viewDidLoad方法:
- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    //1.初始化上下文&設(shè)置當(dāng)前上下文
    /*
     EAGLContext 是蘋果iOS平臺(tái)下實(shí)現(xiàn)OpenGLES 渲染層.
     kEAGLRenderingAPIOpenGLES1 = 1, 固定管線
     kEAGLRenderingAPIOpenGLES2 = 2,
     kEAGLRenderingAPIOpenGLES3 = 3,
     */
    context = [[EAGLContext alloc]initWithAPI:kEAGLRenderingAPIOpenGLES3];
    //判斷context是否創(chuàng)建成功
    if (!context) {
        NSLog(@"Create ES context Failed");
    }
    //設(shè)置當(dāng)前上下文
    [EAGLContext setCurrentContext:context];
    
    //2.獲取GLKView & 設(shè)置context
    GLKView *view =(GLKView *) self.view;
    view.context = context;
    
    //3.設(shè)置背景顏色
    glClearColor(0, 0, 1, 1.0);
    
}
  • 實(shí)現(xiàn)GLView代理方法
//繪制視圖的內(nèi)容
/*
 GLKView對(duì)象使其OpenGL ES上下文成為當(dāng)前上下文,并將其framebuffer綁定為OpenGL ES呈現(xiàn)命令的目標(biāo)。然后,委托方法應(yīng)該繪制視圖的內(nèi)容。
*/
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
{
    glClear(GL_COLOR_BUFFER_BIT);
    
}

運(yùn)行看下結(jié)果,如下圖:


GLKit修改view的backgroundColor

使用GLKit加載圖片紋理并顯示到view

在上面的基礎(chǔ)上添加一個(gè)GLKBaseEffect成員變量*

@interface ViewController ()
{
    EAGLContext *context;
    GLKBaseEffect *cEffect;
}
@end
  • 初始化OpenGL ES
-(void)setUpConfig
{
    //1.初始化上下文&設(shè)置當(dāng)前上下文
    /*
     EAGLContext 是蘋果iOS平臺(tái)下實(shí)現(xiàn)OpenGLES 渲染層.
     kEAGLRenderingAPIOpenGLES1 = 1, 固定管線
     kEAGLRenderingAPIOpenGLES2 = 2,
     kEAGLRenderingAPIOpenGLES3 = 3,
     */
    context = [[EAGLContext alloc]initWithAPI:kEAGLRenderingAPIOpenGLES3];
    //判斷context是否創(chuàng)建成功
    if (!context) {
        NSLog(@"Create ES context Failed");
    }
    //設(shè)置當(dāng)前上下文
    [EAGLContext setCurrentContext:context];
    
    //2.獲取GLKView & 設(shè)置context
    GLKView *view =(GLKView *) self.view;
    view.context = context;
    
    /*3.配置視圖創(chuàng)建的渲染緩存區(qū).
     
     (1). drawableColorFormat: 顏色緩存區(qū)格式.
     簡介:  OpenGL ES 有一個(gè)緩存區(qū),它用以存儲(chǔ)將在屏幕中顯示的顏色。你可以使用其屬性來設(shè)置緩沖區(qū)中的每個(gè)像素的顏色格式。
     
     GLKViewDrawableColorFormatRGBA8888 = 0,
     默認(rèn).緩存區(qū)的每個(gè)像素的最小組成部分(RGBA)使用8個(gè)bit,(所以每個(gè)像素4個(gè)字節(jié),4*8個(gè)bit)。
     
     GLKViewDrawableColorFormatRGB565,
     如果你的APP允許更小范圍的顏色,即可設(shè)置這個(gè)。會(huì)讓你的APP消耗更小的資源(內(nèi)存和處理時(shí)間)
     
     (2). drawableDepthFormat: 深度緩存區(qū)格式
     
     GLKViewDrawableDepthFormatNone = 0,意味著完全沒有深度緩沖區(qū)
     GLKViewDrawableDepthFormat16,
     GLKViewDrawableDepthFormat24,
     如果你要使用這個(gè)屬性(一般用于3D游戲),你應(yīng)該選擇GLKViewDrawableDepthFormat16
     或GLKViewDrawableDepthFormat24。這里的差別是使用GLKViewDrawableDepthFormat16
     將消耗更少的資源
     
     */
    
    //3.配置視圖創(chuàng)建的渲染緩存區(qū).
    view.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888;
    view.drawableDepthFormat = GLKViewDrawableDepthFormat16;
    
    //4.設(shè)置背景顏色
    glClearColor(0, 0, 1, 1.0);
}
  • 創(chuàng)建頂點(diǎn)數(shù)據(jù)
-(void)setUpVertexData
{
    //1.設(shè)置頂點(diǎn)數(shù)組(頂點(diǎn)坐標(biāo),紋理坐標(biāo))
    /*
     紋理坐標(biāo)系取值范圍[0,1];原點(diǎn)是左下角(0,0);
     故而(0,0)是紋理圖像的左下角, 點(diǎn)(1,1)是右上角.
     */
    GLfloat vertexData[] = {
        
        0.5, -0.5, 0.0f,    1.0f, 0.0f, //右下
        0.5, 0.5,  0.0f,    1.0f, 1.0f, //右上
        -0.5, 0.5, 0.0f,    0.0f, 1.0f, //左上
        
        0.5, -0.5, 0.0f,    1.0f, 0.0f, //右下
        -0.5, 0.5, 0.0f,    0.0f, 1.0f, //左上
        -0.5, -0.5, 0.0f,   0.0f, 0.0f, //左下
    };
 
    /*
     頂點(diǎn)數(shù)組: 開發(fā)者可以選擇設(shè)定函數(shù)指針,在調(diào)用繪制方法的時(shí)候,直接由內(nèi)存?zhèn)魅腠旤c(diǎn)數(shù)據(jù),也就是說這部分?jǐn)?shù)據(jù)之前是存儲(chǔ)在內(nèi)存當(dāng)中的,被稱為頂點(diǎn)數(shù)組
     
     頂點(diǎn)緩存區(qū): 性能更高的做法是,提前分配一塊顯存,將頂點(diǎn)數(shù)據(jù)預(yù)先傳入到顯存當(dāng)中。這部分的顯存,就被稱為頂點(diǎn)緩沖區(qū)
     */
    
    //2.開辟頂點(diǎn)緩存區(qū)
    //(1).創(chuàng)建頂點(diǎn)緩存區(qū)標(biāo)識(shí)符ID
    GLuint bufferID;
    glGenBuffers(1, &bufferID);
    //(2).綁定頂點(diǎn)緩存區(qū).(明確作用)
    glBindBuffer(GL_ARRAY_BUFFER, bufferID);
    //(3).將頂點(diǎn)數(shù)組的數(shù)據(jù)copy到頂點(diǎn)緩存區(qū)中(GPU顯存中)
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertexData), vertexData, GL_STATIC_DRAW);
    
    //3.打開讀取通道.
    /*
     (1)在iOS中, 默認(rèn)情況下,出于性能考慮,所有頂點(diǎn)著色器的屬性(Attribute)變量都是關(guān)閉的.
     意味著,頂點(diǎn)數(shù)據(jù)在著色器端(服務(wù)端)是不可用的. 即使你已經(jīng)使用glBufferData方法,將頂點(diǎn)數(shù)據(jù)從內(nèi)存拷貝到頂點(diǎn)緩存區(qū)中(GPU顯存中).
     所以, 必須由glEnableVertexAttribArray 方法打開通道.指定訪問屬性.才能讓頂點(diǎn)著色器能夠訪問到從CPU復(fù)制到GPU的數(shù)據(jù).
     注意: 數(shù)據(jù)在GPU端是否可見,即,著色器能否讀取到數(shù)據(jù),由是否啟用了對(duì)應(yīng)的屬性決定,這就是glEnableVertexAttribArray的功能,允許頂點(diǎn)著色器讀取GPU(服務(wù)器端)數(shù)據(jù)。
   
    (2)方法簡介
    glVertexAttribPointer (GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid* ptr)
   
    功能: 上傳頂點(diǎn)數(shù)據(jù)到顯存的方法(設(shè)置合適的方式從buffer里面讀取數(shù)據(jù))
    參數(shù)列表:
        index,指定要修改的頂點(diǎn)屬性的索引值,例如
        size, 每次讀取數(shù)量。(如position是由3個(gè)(x,y,z)組成,而顏色是4個(gè)(r,g,b,a),紋理則是2個(gè).)
        type,指定數(shù)組中每個(gè)組件的數(shù)據(jù)類型。可用的符號(hào)常量有GL_BYTE, GL_UNSIGNED_BYTE, GL_SHORT,GL_UNSIGNED_SHORT, GL_FIXED, 和 GL_FLOAT,初始值為GL_FLOAT。
        normalized,指定當(dāng)被訪問時(shí),固定點(diǎn)數(shù)據(jù)值是否應(yīng)該被歸一化(GL_TRUE)或者直接轉(zhuǎn)換為固定點(diǎn)值(GL_FALSE)
        stride,指定連續(xù)頂點(diǎn)屬性之間的偏移量。如果為0,那么頂點(diǎn)屬性會(huì)被理解為:它們是緊密排列在一起的。初始值為0
        ptr指定一個(gè)指針,指向數(shù)組中第一個(gè)頂點(diǎn)屬性的第一個(gè)組件。初始值為0
     */
    
    //頂點(diǎn)坐標(biāo)數(shù)據(jù)
    glEnableVertexAttribArray(GLKVertexAttribPosition);
    glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 5, (GLfloat *)NULL + 0);
    
    
    //紋理坐標(biāo)數(shù)據(jù)
    glEnableVertexAttribArray(GLKVertexAttribTexCoord0);
    glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 5, (GLfloat *)NULL + 3);
 
}
  • 為了創(chuàng)建顯示圖片的頂點(diǎn)數(shù)據(jù),確定圖片顯示到view的那個(gè)位置。
    圖片是正方形,我們由兩個(gè)三角形拼起來,所以需要6個(gè)頂點(diǎn)
  • 我們定義頂點(diǎn)坐標(biāo)使用的是一維數(shù)組,我們?cè)谇懊婢椭v過,在OpenGL中習(xí)慣使用一維數(shù)組,所以O(shè)penGL ES也是一樣的。
  • 加載圖片紋理并顯示
-(void)setUpTexture
{
//1.獲取紋理圖片路徑
    NSString *filePath = [[NSBundle mainBundle]pathForResource:@"tutu" ofType:@"jpg"]
    
    //2.設(shè)置紋理參數(shù)
    //紋理坐標(biāo)原點(diǎn)是左下角,但是圖片顯示原點(diǎn)應(yīng)該是左上角.
    NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:@(1),GLKTextureLoaderOriginBottomLeft, nil];
    
    GLKTextureInfo *textureInfo = [GLKTextureLoader textureWithContentsOfFile:filePath options:options error:nil];
    
    //3.使用蘋果GLKit 提供GLKBaseEffect 完成著色器工作(頂點(diǎn)/片元)
    cEffect = [[GLKBaseEffect alloc]init];
    cEffect.texture2d0.enabled = GL_TRUE;
    cEffect.texture2d0.name = textureInfo.name;
}
  • 實(shí)現(xiàn)GLKViewDelegate代理方法
//繪制視圖的內(nèi)容
/*
 GLKView對(duì)象使其OpenGL ES上下文成為當(dāng)前上下文,并將其framebuffer綁定為OpenGL ES呈現(xiàn)命令的目標(biāo)。然后,委托方法應(yīng)該繪制視圖的內(nèi)容。
*/
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
{
    //1.
    glClear(GL_COLOR_BUFFER_BIT);
    
    //2.準(zhǔn)備繪制
    [cEffect prepareToDraw];
    
    //3.開始繪制
    glDrawArrays(GL_TRIANGLES, 0, 6);
    
}
  • viewDidLoad調(diào)用
- (void)viewDidLoad {
    [super viewDidLoad];
    
    //1.OpenGL ES 相關(guān)初始化
    [self setUpConfig];
    
    //2.加載頂點(diǎn)/紋理坐標(biāo)數(shù)據(jù)
    [self setUpVertexData];
    
    //3.加載紋理數(shù)據(jù)(使用GLBaseEffect)
    [self setUpTexture];
}

最后運(yùn)行看下效果,如下圖:


效果圖
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容