OpenGL學習(九)-- OpenGL ES 初探(下)GLKit

個人 OpenGL 專題學習目錄,但願和你們一塊兒學習交流進步!設計模式


1、GLKit 框架簡介

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

  • 不用本身寫着色器: GLKit 框架提供了功能和類,能夠減小建立新的基於着色器的應⽤程序所需的⼯做量,或者支持依賴早期版本的 OpenGL ESOpenGL 提供的固定函數頂點或片斷處理的現有應用程序。
  • 功能:
  • 一、加載紋理
  • 二、提供高性能的數學運算
  • 三、提供常⻅的着⾊器
  • 四、提供視圖以及視圖控制器

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

雖然蘋果棄用 OpenGL ES ,但 iOS 開發者能夠繼續使用。服務器

2、用 GLKit 進行視圖渲染

一、GLKView

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

  • 一、初始化視圖 - (instancetype)initWithFrame:(CGRect)frame context:(EAGLContext *)context; 初始化新視圖。ide

  • 二、設置視圖的代理函數

  • 三、配置幀緩衝區對象 drawableColorFormat顏色緩衝區 的格式 drawableDepthFormat深度緩衝區 的格式 drawableStencilFormat模板緩衝區 的格式 drawableMultisample多重採樣緩衝區 的格式post

  • 四、設置幀緩衝區屬性 drawableHeight底層緩存區對象的高度(以像素爲單位) drawableWidth底層緩存區對象的寬度(以像素爲單位)性能

  • 五、繪製視圖的內容 context 存儲繪製視圖內容時使用的 OpenGL ES 上下文狀態。 - (void)bindDrawable; 將底層 FrameBuffer 對象綁定到 OpenGL ES enableSetNeedsDisplay 布爾值,指定視圖是否響應使得視圖內容無效的消息。 - (void)display; 當即重繪視圖內容。 snapshot UIImage 類型,繪製視圖內容並將其做爲新圖像對象返回。學習

  • 六、刪除視圖 FrameBuffer 對象 - (void)deleteDrawable; 刪除與視圖關聯的可繪製對象。

  • 七、實現 GLKViewDelegate 代理方法 - (void)glkView:(GLKView *)view drawInRect:(CGRect)rect; 繪製視圖內容(必須實現代理

二、GLKViewController

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

  • 一、配置幀速率

    preferredFramesPerSecond 視圖控制器調用視圖以及更新視圖內容的速率,默認爲 30

    framesPerSecond 視圖控制器調用視圖以及更新視圖內容的實際速率。

  • 二、配置 GLKViewController 代理

  • 三、控制幀更新: paused 布爾值,渲染循環是否已暫停。 pauseOnWillResignActive 布爾值,當前程序從新激活活動狀態時視圖控制器是否自動暫停渲染循環。 resumeOnDidBecomeActive 布爾值,當前程序變爲活動狀態時視圖控制是否自動恢復呈現循環。

  • 四、獲取有關 View 的更新信息: framesPerSecond 視圖控制器自建立以來發送的幀更新數。 timeSinceFirstResume 視圖控制器第一次恢復發送更新事件以來通過的時間量。 timeSinceLastResume 自上次視圖控制器恢復發送更新事件以來更新的時間量。 timeSinceLastUpdate 自上次視圖控制器調用委託方法以及通過的時間量。 timeSinceLastDraw 自上次視圖控制器調用視圖 display 方法以來通過的時間量。

  • 五、實現代理方法:

    - (void)glkViewControllerUpdate:(GLKViewController *)controller;處理更新事件

    - (void)glkViewController:(GLKViewController *)controller willPause:(BOOL)pause; 暫停/恢復通知

三、GLKBaseEffect

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

  • 一、命名 Effect:

    labelEffect(效果) 命名。

  • 二、配置模型視圖轉換:

    transform 綁定效果時應用於頂點數據的模型視圖,投影和紋理變換。

  • 三、配置光照效果:

    lightingType 用於計算每一個片斷的光照策略,GLKLightingType

typedef NS_ENUM(GLint, GLKLightingType)
{
    GLKLightingTypePerVertex,
    GLKLightingTypePerPixel
} NS_ENUM_AVAILABLE(10_8, 5_0);
複製代碼

GLKLightingTypePerVertex 表示在三⻆形中每一個頂點執行光照計算,而後在三⻆形進⾏插值。 GLKLightingTypePerPixel 表示光照計算的輸入在三角形內插入,而且在每一個⽚段執行光照計算。

  • 四、配置光照:

    lightModelTwoSided 布爾值,表示爲基元的兩側計算光照。 material 計算渲染圖元光照使⽤的材質屬性。 lightModelAmbientColor 環境顏⾊,應⽤效果渲染的全部圖元。 light0light1light2 分別爲場景中第 一、二、3 個光照屬性。 注意: GLKit 最多就支持3個光照。

  • 五、配置紋理:

    texture2d0readonly 的,第一個紋理屬性。 texture2d1readonly 的,第二個紋理屬性。 textureOrder 紋理應⽤於渲染圖元的順序。 **注意:**最多就支持倆紋理,三個光照,因此 GLKit 有侷限性,若是要支持多個紋理,就不能用 GLKit 了,得本身寫了。

  • 六、配置霧化:

    fog 應用於場景的霧屬性。

  • 七、配置顏色信息:

    colorMaterialEnabled 布爾值,表示計算光照與材質交互時是否使用顏色頂點屬性。 useConstantColor 布爾值,指示是否使用常量顏⾊。 constantColor 不提供每一個頂點顏色數據時使⽤的常量顏⾊。

  • 八、準備繪製效果:

    - (void) prepareToDraw; 準備渲染效果(繪製時同步全部效果更改以保持一致狀態)。注意:繪製以前必須寫

3、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
複製代碼

一、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);
}
複製代碼

二、加載頂點/紋理座標數據

再建立一個方法,命名爲 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 GLKVertexAttribTexCoord, 紋理2 GLKVertexAttribTexCoord1

B、glVertexAttribPointer 方法:

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

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

  • 參數列表:

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

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

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

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

五、stride 參數: 指定連續頂點屬性之間的偏移量。若是爲 0,那麼頂點屬性會被理解爲:它們是緊密排列在一塊兒的。初始值爲 0

六、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);
}
複製代碼

三、加載紋理數據(使用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;
}
複製代碼

四、實現代理方法 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);
}
複製代碼

五、最終調用

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

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

相關文章
相關標籤/搜索