OpenGL學習(九)-- OpenGL ES 初探(下)GLKit
我的
OpenGL
專題學習目錄,希望和大家一起學習交流進步!
- OpenGL學習(一)-- 術語瞭解
- OpenGL學習(二)-- Xcode 搭建 OpenGL 環境
- OpenGL學習(三)-- OpenGL 基礎渲染
- OpenGL學習(四)-- 正面&背面剔除和深度測試
- OpenGL學習(五)-- 裁剪與混合
- OpenGL學習(六)-- 基礎紋理
- OpenGL學習(七)-- 基礎變化綜合練習實踐總結
- OpenGL學習(八)-- OpenGL ES 初探(上)
- OpenGL學習(九)-- OpenGL ES 初探(下)GLKit
- OpenGL學習(十)-- 著色語言 GLSL 語法介紹
- OpenGL學習(十一)-- 用 GLSL 實現載入圖片
- OpenGL學習(十二)-- OpenGL ES 紋理翻轉的策略對比
一、GLKit 框架簡介
GLKit 框架的設計⽬標是為了簡化基於 OpenGL / OpenGL ES 的應用開發。它的出現加快 OpenGL ES 或 OpenGL 應⽤程式開發。使⽤數學庫,背景紋理載入,預先建立的著色器效果,以及標準檢視和檢視控制器來實現渲染迴圈。
- 不用自己寫著色器: GLKit 框架提供了功能和類,可以減少建立新的基於著色器的應⽤程式所需的⼯作量,或者支援依賴早期版本的 OpenGL ES 或 OpenGL 提供的固定函式頂點或片段處理的現有應用程式。
- 功能:
- 1、載入紋理
- 2、提供高效能的數學運算
- 3、提供常⻅的著⾊器
- 4、提供檢視以及檢視控制器
簡單的來說,GLKit 就是為了讓 iOS 開發者在使用OpenGL ES 或 OpenGL 的時候更簡便更容易上手,封裝了一堆庫,我們直接只寫核心程式碼就行了。
雖然蘋果棄用 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 ESenableSetNeedsDisplay
布林值,指定檢視是否響應使得檢視內容無效的訊息。- (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
GLKBaseEffect 是 GLKit 提供的一種簡單的光照/著色系統,用於基於著色器 OpenGL 渲染。
1、命名 Effect:
label
給 Effect(效果) 命名。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
環境顏⾊,應⽤效果渲染的所有圖元。light0
,light1
,light2
分別為場景中第 1、2、3 個光照屬性。 注意: GLKit 最多就支援3個光照。5、配置紋理:
texture2d0
是 readonly 的,第一個紋理屬性。texture2d1
是 readonly 的,第二個紋理屬性。textureOrder
紋理應⽤於渲染圖元的順序。 **注意:**最多就支援倆紋理,三個光照,所以 GLKit 有侷限性,如果要支援多個紋理,就不能用 GLKit 了,得自己寫了。6、配置霧化:
fog
應用於場景的霧屬性。7、配置顏色資訊:
colorMaterialEnabled
布林值,表示計算光照與材質互動時是否使用顏色頂點屬性。useConstantColor
布林值,指示是否使用常量顏⾊。constantColor
不提供每個頂點顏色資料時使⽤的常量顏⾊。8、準備繪製效果:
- (void) prepareToDraw;
準備渲染效果(繪製時同步所有效果更改以保持一致狀態)。注意:繪製之前必須寫。
三、GLKit 牛刀小試
思維導圖如下:
首先我建立一個帶預設 storyboard 的工程,為了方便,直接把自帶的 View 的 Class型別改為了 GLKView,當然我們也可以用程式碼 alloc 建立。
然後我們在 .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>
複製程式碼
定義兩個全域性變數 EAGLContext 和 GLKBaseEffect
@interface ViewController ()
{
EAGLContext *context;
GLKBaseEffect *cEffect;
}
@end
複製程式碼
1、OpenGL ES 相關初始化
先來建立一個方法,命名為 setUpConfig,用來進行 OpenGL ES 的相關初始化
1)、初始化上下文 & 設定當前上下文
- 初始化上下文:
context = [[EAGLContext alloc]initWithAPI:kEAGLRenderingAPIOpenGLES3]; 複製程式碼
EAGLContext
是蘋果 iOS 平臺下實現 OpenGL ES 渲染層。 引數代表使用哪種 OpenGL ES 的 API 初始化,OpenGL ES 1 使用的是固定管線,2 和 3 差別不大。
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) 使用 8 個 bit,(所以每個畫素 4 個位元組,4 * 8 個 bit)。GLKViewDrawableColorFormatRGB565
, 如果你的 APP 允許更小範圍的顏色,即可設定這個。會讓你的 APP 消耗更小的資源(記憶體和處理時間)
(2). drawableDepthFormat
:深度快取區格式
view.drawableDepthFormat = GLKViewDrawableDepthFormat16; 複製程式碼
GLKViewDrawableDepthFormatNone = 0
, 意味著完全沒有深度緩衝區GLKViewDrawableDepthFormat16
,GLKViewDrawableDepthFormat24
, 如果你要使用這個屬性(一般用於 3D 遊戲),你應該選擇GLKViewDrawableDepthFormat16
或GLKViewDrawableDepthFormat24
。這裡的差別是使用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,1.0);
}
複製程式碼
2、載入頂點/紋理座標資料
再建立一個方法,命名為 setUpVertexData,用來載入頂點/紋理座標資料。
1)設定頂點陣列(頂點座標和紋理座標):
我這裡是把頂點座標和紋理座標放到了一個數組裡,當然你也可以分別放到兩個數組裡,但是我覺得一個數組後續操作更方便些,如果是兩個陣列,還要開闢兩個緩衝區。
GLfloat vertexData[] = { 0.5,-0.5,0.0f,1.0f,//右下 0.5,0.5,-0.0f,//右上 -0.5,//左上 0.5,//右下 -0.5,//左上 -0.5,//左下 }; 複製程式碼
這裡一共是兩個三角形組成的,所以是六個頂點。前三個元素組成頂點座標,第四個和第五個元素組成二維的紋理座標,後面以此類推。 紋理座標系取值範圍 [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
, 紋理1GLKVertexAttribTexCoord
, 紋理2GLKVertexAttribTexCoord1
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,那麼頂點屬性會被理解為:它們是緊密排列在一起的。初始值為 06、
ptr
引數: 指定一個指標,指向陣列中第一個頂點屬性的第一個元件。初始值為 0
下面為 setUpVertexData 方法完整程式碼:
- (void)setUpVertexData {
// 1.設定頂點陣列(頂點座標,紋理座標)
GLfloat vertexData[] = {
0.5,//右下
0.5,//右上
-0.5,//左上
0.5,//右下
-0.5,//左上
-0.5,//左下
};
//2.開闢頂點快取區
//(1).建立頂點快取區識別符號ID
GLuint bufferID;
glGenBuffers(1,&bufferID);
//(2).繫結頂點快取區.(明確作用)
glBindBuffer(GL_ARRAY_BUFFER,bufferID);
//(3).將頂點陣列的資料copy到頂點快取區中(GPU視訊記憶體中)
glBufferData(GL_ARRAY_BUFFER,GL_STATIC_DRAW);
//3.開啟讀取通道.
//頂點座標資料
glEnableVertexAttribArray(GLKVertexAttribPosition);
glVertexAttribPointer(GLKVertexAttribPosition,3,GL_FLOAT,GL_FALSE,sizeof(GLfloat) * 5,(GLfloat *)NULL + 0);
//紋理座標資料
glEnableVertexAttribArray(GLKVertexAttribTexCoord0);
glVertexAttribPointer(GLKVertexAttribTexCoord0,2,(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,6);
}
複製程式碼
5、最終呼叫
- (void)viewDidLoad {
[super viewDidLoad];
//1.OpenGL ES 相關初始化
[self setUpConfig];
//2.載入頂點/紋理座標資料
[self setUpVertexData];
//3.載入紋理資料(使用GLBaseEffect)
[self setUpTexture];
}
複製程式碼
轉載請備註原文出處,不得用於商業傳播——凡幾多