OpenGL ES初探(下)—— GLKit

一、GLKit 框架簡介

GLKit 框架的設計?標是為了簡化基于 OpenGL / OpenGL ES 的應用開發。它的出現加快 OpenGL ESOpenGL 應?程序開發。使?數學庫,背景紋理加載,預先創建的著色器效果,以及標準視圖和視圖控制器來實現渲染循環。

  • 不用自己寫著色器:
    GLKit 框架提供了功能和類,可以減少創建新的基于著色器的應?程序所需的?作量,或者支持依賴早期版本的 OpenGL ESOpenGL 提供的固定函數頂點或片段處理的現有應用程序。
  • 功能:
    1、加載紋理
    2、提供高性能的數學運算
    3、提供常?的著?器
    4、提供視圖以及視圖控制器

簡單的來說,GLKit 就是為了讓 iOS 開發者在使用OpenGL ESOpenGL 的時候更簡便更容易上手,封裝了一堆庫,我們直接只寫核心代碼就行了。

雖然蘋果棄用 OpenGL ES ,但 iOS 開發者可以繼續使用。

二、用 GLKit 進行視圖渲染

1、GLKView

GLKView 繼承 UIView,提供繪制場所(View)。
下面看一下 GLKView 使用 OpenGL ES 繪制內容的視圖默認實現:

  • 1、初始化視圖
    - (instancetype)initWithFrame:(CGRect)frame context:(EAGLContext *)context;初始化新視圖。
  • 2、設置視圖的代理
  • 3、配置幀緩沖區對象
    drawableColorFormat顏色緩沖區 的格式
    drawableDepthFormat深度緩沖區 的格式
    drawableStencilFormat模板緩沖區 的格式
    drawableMultisample多重采樣緩沖區 的格式
  • 4、設置幀緩沖區屬性
    drawableHeight底層緩存區對象的高度(以像素為單位)
    drawableWidth底層緩存區對象的寬度(以像素為單位)
  • 5、繪制視圖的內容
    context 存儲繪制視圖內容時使用的 OpenGL ES 上下文狀態。
    - (void)bindDrawable; 將底層 FrameBuffer 對象綁定到 OpenGL ES
    enableSetNeedsDisplay 布爾值,指定視圖是否響應使得視圖內容無效的消息。
    - (void)display; 立即重繪視圖內容。
    snapshot UIImage 類型,繪制視圖內容并將其作為新圖像對象返回。
  • 6、刪除視圖 FrameBuffer 對象
    - (void)deleteDrawable; 刪除與視圖關聯的可繪制對象。
  • 7、實現 GLKViewDelegate 代理方法
    - (void)glkView:(GLKView *)view drawInRect:(CGRect)rect;
    繪制視圖內容(必須實現代理

2、GLKViewController

GLKViewController 繼承 UIViewController,(擴展于標準的 UIKit 設計模式,用于繪制視圖內容的管理與呈現)

  • 1、配置幀速率
  • preferredFramesPerSecond 視圖控制器調用視圖以及更新視圖內容的速率,默認為30。
  • framesPerSecond 視圖控制器調用視圖以及更新視圖內容的實際速率。
  • 2、配置 GLKViewController 代理
  • 3、控制幀更新:
    paused 布爾值,渲染循環是否已暫停。
    pauseOnWillResignActive 布爾值,當前程序重新激活活動狀態時視圖控制器是否自動暫停渲染循環。
    resumeOnDidBecomeActive 布爾值,當前程序變為活動狀態時視圖控制是否自動恢復呈現循環。
  • 4、獲取有關 View 的更新信息:
    framesPerSecond 視圖控制器自創建以來發送的幀更新數。
    timeSinceFirstResume 視圖控制器第一次恢復發送更新事件以來經過的時間量。
    timeSinceLastResume 自上次視圖控制器恢復發送更新事件以來更新的時間量。
    timeSinceLastUpdate 自上次視圖控制器調用委托方法以及經過的時間量。
    timeSinceLastDraw 自上次視圖控制器調用視圖 display 方法以來經過的時間量。
  • 5、實現代理方法:
    - (void)glkViewControllerUpdate:(GLKViewController *)controller; 處理更新事件
    - (void)glkViewController:(GLKViewController *)controller willPause:(BOOL)pause; 暫停/恢復通知

3、GLKBaseEffect

GLKBaseEffectGLKit 提供的一種簡單的光照/著色系統,用于基于著色器 OpenGL 渲染。

  • 1、命名 Effect:
    labelEffect(效果)命名。
  • 2、配置模型視圖轉換:
    transform 綁定效果時應用于頂點數據的模型視圖,投影和紋理變換。
  • 3、配置光照效果:
    lightingType 用于計算每個片段的光照策略,GLKLightingType
typedef NS_ENUM(GLint, GLKLightingType)
{
    GLKLightingTypePerVertex,
    GLKLightingTypePerPixel
} NS_ENUM_AVAILABLE(10_8, 5_0);

GLKLightingTypePerVertex 表示在三?形中每個頂點執行光照計算,然后在三?形進?插值。
GLKLightingTypePerPixel 表示光照計算的輸入在三角形內插入,并且在每個?段執行光照計算。

  • 4、配置光照:
    lightModelTwoSided 布爾值,表示為基元的兩側計算光照。
    material 計算渲染圖元光照使?的材質屬性。
    lightModelAmbientColor 環境顏?,應?效果渲染的所有圖元。
    light0light1light2 分別為場景中第 1、2、3 個光照屬性。
    注意: GLKit 最多就支持3個光照。
  • 5、配置紋理:
    texture2d0readonly 的,第一個紋理屬性。
    texture2d1readonly 的,第二個紋理屬性。
    textureOrder 紋理應?于渲染圖元的順序。
    注意:最多就支持倆紋理,三個光照,所以 GLKit 有局限性,如果要支持多個紋理,就不能用 GLKit 了,得自己寫了。
  • 6、配置霧化:
    fog 應用于場景的霧屬性。
  • 7、配置顏色信息:
    colorMaterialEnabled 布爾值,表示計算光照與材質交互時是否使用顏色頂點屬性。
    useConstantColor 布爾值,指示是否使用常量顏?。
    constantColor 不提供每個頂點顏色數據時使?的常量顏?。
  • 8、準備繪制效果:
    - (void) prepareToDraw; 準備渲染效果(繪制時同步所有效果更改以保持一致狀態)。注意:繪制之前必須寫

二、GLKit 牛刀小試

思維導圖如下:

OpenGL ES GLKit 圖片加載 by 凡幾多.png

首先我創建一個帶默認 storyboard 的工程,為了方便,直接把自帶的 ViewClass類型改為了 GLKView,當然我們也可以用代碼 alloc 創建。

GLKView.png

然后我們在 .h 文件中導入頭文件 GLKit,并且把 ViewController 的父類改為 GLKViewController

#import <UIKit/UIKit.h>
#import <GLKit/GLKit.h>

@interface ViewController : GLKViewController

接下來在 .m 文件中導入頭文件。

#import <OpenGLES/ES3/gl.h>
#import <OpenGLES/ES3/glext.h>

定義兩個全局變量 EAGLContextGLKBaseEffect

@interface ViewController ()
{
    EAGLContext *context;
    GLKBaseEffect *cEffect;
}
@end

1、OpenGL ES 相關初始化

先來創建一個方法,命名為 setUpConfig,用來進行 OpenGL ES 的相關初始化

1)、初始化上下文 & 設置當前上下文
  • 初始化上下文:
context = [[EAGLContext alloc]initWithAPI:kEAGLRenderingAPIOpenGLES3];

EAGLContext 是蘋果 iOS 平臺下實現 OpenGL ES 渲染層。
參數代表使用哪種 OpenGL ESAPI 初始化,OpenGL ES 1 使用的是固定管線23 差別不大。
kEAGLRenderingAPIOpenGLES1 = 1, kEAGLRenderingAPIOpenGLES2 = 2,
kEAGLRenderingAPIOpenGLES3 = 3。

  • 設置當前上下文:
[EAGLContext setCurrentContext:context];
2)、獲取GLKView & 設置context
GLKView *view = (GLKView *) self.view;
view.context = context;
3)、配置視圖創建的渲染緩存區

(1). drawableColorFormat:顏色緩存區格式
簡介:OpenGL ES 有一個緩存區,它用以存儲將在屏幕中顯示的顏色。你可以使用其屬性來設置緩沖區中的每個像素的顏色格式。

view.drawableColorFormat = >GLKViewDrawableColorFormatRGBA8888;

GLKViewDrawableColorFormatRGBA8888 = 0,
默認緩存區的每個像素的最小組成部分(RGBA)使用 8bit,(所以每個像素 4 個字節,4 * 8bit)。
GLKViewDrawableColorFormatRGB565,如果你的 APP 允許更小范圍的顏色,即可設置這個。會讓你的 APP 消耗更小的資源(內存和處理時間)

(2). drawableDepthFormat:深度緩存區格式

view.drawableDepthFormat = GLKViewDrawableDepthFormat16;

GLKViewDrawableDepthFormatNone = 0,意味著完全沒有深度緩沖區
GLKViewDrawableDepthFormat16,
GLKViewDrawableDepthFormat24,
如果你要使用這個屬性(一般用于 3D 游戲),你應該選擇GLKViewDrawableDepthFormat16GLKViewDrawableDepthFormat24。這里的差別是使用 GLKViewDrawableDepthFormat16 將消耗更少的資源。

下面為 setUpConfig 方法完整代碼:

- (void)setUpConfig {
    // 1.初始化上下文&設置當前上下文
    context = [[EAGLContext alloc]initWithAPI:kEAGLRenderingAPIOpenGLES3];
    //判斷context是否創建成功
    if (!context) {
        NSLog(@"Create ES context Failed");
    }
    //設置當前上下文
    [EAGLContext setCurrentContext:context];
    
    //2.獲取GLKView & 設置context
    GLKView *view =(GLKView *) self.view;
    view.context = context;
        
    //3.配置視圖創建的渲染緩存區.
    view.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888;
    view.drawableDepthFormat = GLKViewDrawableDepthFormat16;
    
    //4.設置背景顏色
    glClearColor(1, 0, 0, 1.0);
}

2、加載頂點/紋理坐標數據

再創建一個方法,命名為 setUpVertexData,用來加載頂點/紋理坐標數據。

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, //左下
   };

這里一共是兩個三角形組成的,所以是六個頂點。前三個元素組成頂點坐標,第四個和第五個元素組成二維的紋理坐標,后面以此類推。
紋理坐標系取值范圍 [0,1];原點是左下角 (0,0);故而 (0,0) 是紋理圖像的左下角, 點 (1,1) 是右上角.

2)開辟頂點緩存區:
  • 頂點數組:開發者可以選擇設定函數指針,在調用繪制方法的時候,直接由內存傳入頂點數據,也就是說這部分數據之前是存儲在內存當中的,被稱為頂點數組
  • 頂點緩存區:性能更高的做法是,提前分配一塊顯存,將頂點數據預先傳入到顯存當中。這部分的顯存,就被稱為頂點緩沖區。

(1)創建頂點緩沖區標識符 ID

GLuint bufferID;
glGenBuffers(1, &bufferID);

glGenBuffers(GLsizei n, GLuint *buffers)的第一個參數是表明有 1 個緩沖區。頂點緩沖對象(Vertex Buffer Objects, VBO)

(2)綁定頂點緩存區(明確作用)

glBindBuffer(GL_ARRAY_BUFFER, bufferID);

glBindBuffer(GLenum target, GLuint buffer)的第一個參數代表是做什么用的,GL_ARRAY_BUFFER 代表數組緩沖區。

(3)將頂點數組的數據 copy 到頂點緩存區中(內存——>GPU 顯存中)

glBufferData(GL_ARRAY_BUFFER, sizeof(vertexData), vertexData, GL_STATIC_DRAW);

glBufferData(GLenum target, GLsizeiptr size, const GLvoid *data, GLenum usage)
target 參數:指定是什么類型的數據,和上面 glBindBuffer中的保持一致。
size 參數:這個數據有多大。
data 參數:數據的地址。這里因為是數組,所以數組名就是它的首地址。
usage 參數:繪制方式,靜態繪制還是動態繪制。

3)打開讀取通道:

(1)默認是關閉的

iOS 中, 默認情況下,出于性能考慮,所有頂點著色器的屬性(Attribute)變量都是關閉的。
意味著,頂點數據在著色器端(服務端)是不可用的。即使你已經使用 glBufferData 方法,將頂點數據從內存拷貝到頂點緩存區中(GPU 顯存中)。
所以,必須由 glEnableVertexAttribArray 方法打開通道,指定訪問屬性,才能讓頂點著色器能夠訪問到從 CPU 復制到 GPU 的數據。

注意:數據在 GPU 端是否可見,即著色器能否讀取到數據,由是否啟用了對應的屬性決定,這就是glEnableVertexAttribArray的功能,允許頂點著色器讀取 GPU(服務器端)數據。

(2)方法簡介
A、glEnableVertexAttribArray 方法:

glEnableVertexAttribArray(GLuint index)

功能:打開對應 attribute 通道的開關。

index 參數:代表屬性通道
頂點 GLKVertexAttribPosition,
法線 GLKVertexAttribNormal,
顏色值 GLKVertexAttribColor,
紋理1 GLKVertexAttribTexCoord0,
紋理2 GLKVertexAttribTexCoord1

B、glVertexAttribPointer 方法:

glVertexAttribPointer (GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid* ptr)

功能:上傳頂點數據到顯存的方法(設置合適的方式從buffer里面讀取數據)

  • 參數列表:

1、indx 參數:指定要修改的頂點屬性的索引值

2、size 參數:每次讀取數量(步長)。(如 position 是由3(x,y,z)組成,而顏色是4(r,g,b,a),紋理則是2個)

3、type 參數:指定數組中每個組件的數據類型。可用的符號常量有 GL_BYTE,GL_UNSIGNED_BYTE,GL_SHORT,GL_UNSIGNED_SHORT,GL_FIXED, 和 GL_FLOAT,初始值為 GL_FLOAT

4、normalized 參數:指定當被訪問時,固定點數據值是否應該被歸一化(GL_TRUE)或者直接轉換為固定點值(GL_FALSE),一般設為 GL_FALSE

5、stride 參數:指定連續頂點屬性之間的偏移量。如果為 0,那么頂點屬性會被理解為:它們是緊密排列在一起的。初始值為 0

6、ptr 參數:指定一個指針,指向數組中第一個頂點屬性的第一個組件。初始值為0

下面為 setUpVertexData 方法完整代碼:

- (void)setUpVertexData {
    // 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, //左下
    };
    //2.開辟頂點緩存區
    //(1).創建頂點緩存區標識符ID
    GLuint bufferID;
    glGenBuffers(1, &bufferID);
    //(2).綁定頂點緩存區.(明確作用)
    glBindBuffer(GL_ARRAY_BUFFER, bufferID);
    //(3).將頂點數組的數據copy到頂點緩存區中(GPU顯存中)
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertexData), vertexData, GL_STATIC_DRAW);
    
    //3.打開讀取通道.
    //頂點坐標數據
    glEnableVertexAttribArray(GLKVertexAttribPosition);
    glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 5, (GLfloat *)NULL + 0);
    
    //紋理坐標數據
    glEnableVertexAttribArray(GLKVertexAttribTexCoord0);
    glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 5, (GLfloat *)NULL + 3);
}

3、加載紋理數據(使用GLBaseEffect)

再創建一個方法,命名為 setUpTexture,用來加載紋理數據(使用 GLBaseEffect)。

注意:
因為紋理原點是:左下角(0,0)
view 原點是:左上角(0,0)
所以在設置紋理的 options 參數時,需要傳 GLKTextureLoaderOriginBottomLeft 翻轉一下,不然紋理就是倒著的。這是 GLKit 里的解決辦法,在 OpenGL ES 里就沒這么方便了。

- (void)setUpTexture {
    //1.獲取紋理圖片路徑
    NSString *filePath = [[NSBundle mainBundle]pathForResource:@"凡幾多" ofType:@"jpg"];
    
    //2.設置紋理參數
    //紋理坐標原點是左下角,但是圖片顯示原點應該是左上角.
    NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:@(1),GLKTextureLoaderOriginBottomLeft, nil];
    
    GLKTextureInfo *textureInfo = [GLKTextureLoader textureWithContentsOfFile:filePath options:options error:nil];
    
    //3.使用蘋果GLKit 提供GLKBaseEffect 完成著色器工作(頂點/片元)
    cEffect = [[GLKBaseEffect alloc]init];
    cEffect.texture2d0.enabled = GL_TRUE;
    cEffect.texture2d0.name = textureInfo.name;
}

4、實現代理方法 GLKViewDelegate

GLKView 對象使其 OpenGL ES 上下文成為當前上下文,并將其 framebuffer 綁定為 OpenGL ES 呈現命令的目標。然后,委托方法應該繪制視圖的內容。

//繪制視圖的內容
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect {
    //1.
    glClear(GL_COLOR_BUFFER_BIT);
    
    //2.準備繪制
    [cEffect prepareToDraw];
    
    //3.開始繪制,用三角形,從第0個頂點開始畫,一共畫6個
    glDrawArrays(GL_TRIANGLES, 0, 6);
}

5、最終調用

- (void)viewDidLoad {
    [super viewDidLoad];
    //1.OpenGL ES 相關初始化
    [self setUpConfig];
    
    //2.加載頂點/紋理坐標數據
    [self setUpVertexData];
    
    //3.加載紋理數據(使用GLBaseEffect)
    [self setUpTexture];  
}

轉載請備注原文出處,不得用于商業傳播——凡幾多

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,238評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,430評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,134評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,893評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,653評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,136評論 1 323
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,212評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,372評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,888評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,738評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,939評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,482評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,179評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,588評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,829評論 1 283
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,610評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,916評論 2 372

推薦閱讀更多精彩內容