iOS中基于OpenGLES的功能實(shí)現(xiàn)或框架
SpriteKit- 用來(lái)創(chuàng)建2D游戲而優(yōu)化硬件加速動(dòng)畫系統(tǒng)
SceneKit
Metal
ARKit
CoreImage
GPUIImage
CoreAnimation
有趣的是,OpenGLES 并沒有提供窗口層,托管系統(tǒng)必須提供函數(shù)來(lái)創(chuàng)建一個(gè)能夠接受OpenGLES命令的渲染上下文,以及寫入任何繪圖命令結(jié)果緩沖區(qū)
GLKit 是蘋果提供給OpenGLES來(lái)使用的類似UIKit的框架,如:
GLKView
GLKViewController
創(chuàng)建OpenGLES第一個(gè)程序
1.將Main.storyboard的view屬性改為GLKView

2.ViewController頭文件修改繼承自GLKViewController,并導(dǎo)入OpenGLES相關(guān)頭文件,定義上下文context,著色器或光照mEffect,其中:
EAGLContent是蘋果在ios平臺(tái)下實(shí)現(xiàn)的opengles渲染層,用于渲染結(jié)果在目標(biāo)surface上的更新。
GLKBaseEffect:著色器或者光照
#import <GLKit/GLKit.h>
#import <OpenGLES/ES3/gl.h>
#import <OpenGLES/ES3/glext.h>
@interface ViewController : GLKViewController
{
EAGLContext *context;
GLKBaseEffect *mEffect;
}
@end
3,配置OpenGLES 關(guān)聯(lián)上下文:
在創(chuàng)建上下文的過(guò)程中我們應(yīng)該知道,OpenGLES有三個(gè)版本的API,對(duì)應(yīng)關(guān)系如下
kEAGLRenderingAPIOpenGLES1 ->OpenGL ES 1.0,固定管線
kEAGLRenderingAPIOpenGLES2 ->OpenGL ES 2.0
kEAGLRenderingAPIOpenGLES3 ->OpenGL ES 3.0
其中OpenGL ES 1.0使用的是固定管線,功能雖然沒有2.0,3.0版本那么強(qiáng)大,但是勝在方便使用,學(xué)會(huì)固定管線之后再去學(xué)習(xí)變化管線也很容易上手
-(void)setUpConfig
{
//context
//EAGLContent是蘋果在ios平臺(tái)下實(shí)現(xiàn)的opengles渲染層,用于渲染結(jié)果在目標(biāo)surface上的更新
//新建OpenGL ES上下文
context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];//這里用的是opengles3.
if (!context) {
NSLog(@"Failed to create ES context");
}
//創(chuàng)建一個(gè)OpenGL ES上下文并將其分配給從storyboard加載的視圖
//注意:這里需要把stroyBoard記得添加為GLKView
GLKView * view = (GLKView *)self.view;
view.context = context;
//配置視圖創(chuàng)建的渲染緩沖區(qū):顏色緩沖區(qū)
view.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888;
//配置視圖創(chuàng)建的渲染緩沖區(qū):深度緩沖區(qū)
view.drawableDepthFormat = GLKViewDrawableDepthFormat24;
//配置視圖創(chuàng)建的渲染緩沖區(qū):模板緩沖區(qū)
// view.drawableStencilFormat = GLKViewDrawableStencilFormat8;
//啟用多重采樣
//view.drawableMultisample = GLKViewDrawableMultisample4X;
[EAGLContext setCurrentContext:context];
glEnable(GL_DEPTH_TEST); //開啟深度測(cè)試,就是讓離你近的物體可以遮擋離你遠(yuǎn)的物體。
glClearColor(0.1, 0.2, 0.3, 1); //設(shè)置surface的清除顏色,也就是渲染到屏幕上的背景色。
}
顏色緩沖區(qū)
OpenGL ES 有一個(gè)緩存區(qū),它用以存儲(chǔ)將在屏幕中顯示的顏色。你可以使用其屬性來(lái)設(shè)置緩沖區(qū)中的每個(gè)像素的顏色格式。
默認(rèn):GLKViewDrawableColorFormatRGBA8888,即緩存區(qū)的每個(gè)像素的最小組成部分(RGBA)使用8個(gè)bit,(所以每個(gè)像素4個(gè)字節(jié),4*8個(gè)bit)。
GLKViewDrawableColorFormatRGB565,如果你的APP允許更小范圍的顏色,即可設(shè)置這個(gè)。會(huì)讓你的APP消耗更小的資源(內(nèi)存和處理時(shí)間)
深度緩沖區(qū)
OpenGL ES 另一個(gè)緩存區(qū),深度緩沖區(qū)。幫助我們確??梢愿咏^察者的對(duì)象顯示在遠(yuǎn)一些的對(duì)象前面。離觀察者近一些的對(duì)象會(huì)擋住在它后面的對(duì)象)
默認(rèn):OpenGL把接近觀察者的對(duì)象的所有像素存儲(chǔ)到深度緩沖區(qū),當(dāng)開始繪制一個(gè)像素時(shí),它(OpenGL)首先檢查深度緩沖區(qū),看是否已經(jīng)繪制了更接近觀察者的什么東西,如果是則忽略它(要繪制的像素,就是說(shuō),在繪制一個(gè)像素之前,看看前面有沒有擋著它的東西,如果有那就不用繪制了)。否則,把它增加到深度緩沖區(qū)和顏色緩沖區(qū)。
缺省值是GLKViewDrawableDepthFormatNone,意味著完全沒有深度緩沖區(qū)。
但是如果你要使用這個(gè)屬性(一般用于3D游戲),你應(yīng)該選擇GLKViewDrawableDepthFormat16或GLKViewDrawableDepthFormat24。
這里的差別是使用GLKViewDrawableDepthFormat16將消耗更少的資源,但是當(dāng)對(duì)象非常接近彼此時(shí),你可能存在渲染問題()
模板緩沖區(qū)
你的OpenGL上下文的另一個(gè)可選的緩沖區(qū)是stencil(模板)緩沖區(qū)。它幫助你把繪制區(qū)域限定到屏幕的一個(gè)特定部分。它還用于像影子一類的事物=比如你可以使用stencil緩沖區(qū)確保影子投射到地板。
缺省值是GLKViewDrawableStencilFormatNone,意思是沒有stencil緩沖區(qū),但是你可以通過(guò)設(shè)置其值為GLKViewDrawableStencilFormat8(唯一的其他選項(xiàng))使用它
多重采樣
這是你可以設(shè)置的最后一個(gè)可選緩沖區(qū),對(duì)應(yīng)的GLKView屬性是multisampling。
如果你曾經(jīng)嘗試過(guò)使用OpenGL畫線并關(guān)注過(guò)"鋸齒壯線",multisampling就可以幫助你處理以前對(duì)于每個(gè)像素,都會(huì)調(diào)用一次fragment shader(片段著色器)
drawableMultisample基本上替代了這個(gè)工作,它將一個(gè)像素分成更小的單元,并在更細(xì)微的層面上多次調(diào)用fragment shader。
之后它將返回的顏色合并,生成更光滑的幾何邊緣效果。
要小心此操作,因?yàn)樗枰加媚愕腶pp的更多的處理時(shí)間和內(nèi)存。
缺省值是GLKViewDrawableMultisampleNone,但是你可以通過(guò)設(shè)置其值GLKViewDrawableMultisample4X為來(lái)使用它
4.加載頂點(diǎn)數(shù)據(jù)
-(void)uploadVertexArray
{
//第一步:設(shè)置頂點(diǎn)數(shù)組
//OpenGLES的世界坐標(biāo)系是[-1, 1],故而點(diǎn)(0, 0)是在屏幕的正中間。
//頂點(diǎn)數(shù)據(jù),前3個(gè)是頂點(diǎn)坐標(biāo)x,y,z;后面2個(gè)是紋理坐標(biāo)。
//紋理坐標(biāo)系的取值范圍是[0, 1],原點(diǎn)是在左下角。故而點(diǎn)(0, 0)在左下角,點(diǎn)(1, 1)在右上角
//2個(gè)三角形構(gòu)成
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)緩沖區(qū)
//頂點(diǎn)緩存區(qū)
GLuint buffer;
//申請(qǐng)一個(gè)緩存區(qū)標(biāo)識(shí)符
glGenBuffers(1, &buffer);
//glBindBuffer把標(biāo)識(shí)符綁定到GL_ARRAY_BUFFER上
glBindBuffer(GL_ARRAY_BUFFER, buffer);
//glBufferData把頂點(diǎn)數(shù)據(jù)從cpu內(nèi)存復(fù)制到gpu內(nèi)存
glBufferData(GL_ARRAY_BUFFER, sizeof(vertexData), vertexData, GL_STATIC_DRAW);
//第三步:設(shè)置合適的格式從buffer里面讀取數(shù)據(jù))
/*
默認(rèn)情況下,出于性能考慮,所有頂點(diǎn)著色器的屬性(Attribute)變量都是關(guān)閉的,意味著數(shù)據(jù)在著色器端是不可見的,哪怕數(shù)據(jù)已經(jīng)上傳到GPU,由glEnableVertexAttribArray啟用指定屬性,才可在頂點(diǎn)著色器中訪問逐頂點(diǎn)的屬性數(shù)據(jù)。glVertexAttribPointer或VBO只是建立CPU和GPU之間的邏輯連接,從而實(shí)現(xiàn)了CPU數(shù)據(jù)上傳至GPU。但是,數(shù)據(jù)在GPU端是否可見,即,著色器能否讀取到數(shù)據(jù),由是否啟用了對(duì)應(yīng)的屬性決定,這就是glEnableVertexAttribArray的功能,允許頂點(diǎn)著色器讀取GPU(服務(wù)器端)數(shù)據(jù)。
*/
glEnableVertexAttribArray(GLKVertexAttribPosition);
//glVertexAttribPointer 使用來(lái)上傳頂點(diǎn)數(shù)據(jù)到顯卡的方法(設(shè)置合適的格式從buffer里面讀取數(shù)據(jù))
// index: 指定要修改的頂點(diǎn)屬性的索引值
// size : 指定每個(gè)頂點(diǎn)屬性的組件數(shù)量。必須為1、2、3或者4。初始值為4。(如position是由3個(gè)(x,y,z)組成,而顏色是4個(gè)(r,g,b,a))
// 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 這個(gè)值受到VBO的影響
/*
VBO,頂點(diǎn)緩存對(duì)象
在不使用VBO的情況下:事情是這樣的,ptr就是一個(gè)指針,指向的是需要上傳到頂點(diǎn)數(shù)據(jù)指針。通常是數(shù)組名的偏移量。
在使用VBO的情況下:首先要glBindBuffer,以后ptr指向的就不是具體的數(shù)據(jù)了。因?yàn)閿?shù)據(jù)已經(jīng)緩存在緩沖區(qū)了。這里的ptr指向的是緩沖區(qū)數(shù)據(jù)的偏移量。這里的偏移量是整型,但是需要強(qiáng)制轉(zhuǎn)換為const GLvoid *類型傳入。注意的是,這里的偏移的意思是數(shù)據(jù)個(gè)數(shù)總寬度數(shù)值。
比如說(shuō):這里存放的數(shù)據(jù)前面有3個(gè)float類型數(shù)據(jù),那么這里的偏移就是,3*sizeof(float).
最后解釋一下,glVertexAttribPointer的工作原理:
首先,通過(guò)index得到著色器對(duì)應(yīng)的變量openGL會(huì)把數(shù)據(jù)復(fù)制給著色器的變量。
以后,通過(guò)size和type知道當(dāng)前數(shù)據(jù)什么類型,有幾個(gè)。openGL會(huì)映射到float,vec2, vec3 等等。
由于每次上傳的頂點(diǎn)數(shù)據(jù)不止一個(gè),可能是一次4,5,6頂點(diǎn)數(shù)據(jù)。那么通過(guò)stride就是在數(shù)組中間隔多少byte字節(jié)拿到下個(gè)頂點(diǎn)此類型數(shù)據(jù)。
最后,通過(guò)ptr的指針在迭代中獲得所有數(shù)據(jù)。
那么,最最后openGL如何知道ptr指向的數(shù)組有多長(zhǎng),讀取幾次呢。是的,openGL不知道。所以在調(diào)用繪制的時(shí)候,需要傳入一個(gè)count數(shù)值,就是告訴openGL繪制的時(shí)候迭代幾次glVertexAttribPointer調(diào)用。
*/
//(GLfloat *)NULL + 0 指針,指向數(shù)組首地址
glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 5, (GLfloat *)NULL + 0);
//紋理
glEnableVertexAttribArray(GLKVertexAttribTexCoord0);
//(GLfloat *)NULL + 3,指向到紋理數(shù)據(jù)
glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 5, (GLfloat * )NULL + 3);
}
全屏顯示圖片的方法
/*
怎樣讓圖片全屏顯示
1.0f,-1.0f,0.0f, 1.0f,0.0f,//右下
1.0f,1.0f,0.0f, 1.0f,1.0f,//右上
-1.0f,1.0f,0.0f, 0.0f,1.0f,//左上
1.0f,-1.0f,0.0f, 1.0f,0.0f,//右下
-1.0f,1.0f,0.0f, 0.0f,1.0f,//左上
-1.0f,-1.0f,0.0f, 0.0f,0.0f,//左下
*/
5.加載紋理
-(void)uploadTexture
{
//第一步,獲取紋理圖片保存路徑
NSString *filePath = [[NSBundle mainBundle]pathForResource:@"cTest" ofType:@"jpg"];
//GLKTextureLoaderOriginBottomLeft,紋理坐標(biāo)是相反的
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:@(1), GLKTextureLoaderOriginBottomLeft,NULL];
GLKTextureInfo *textureInfo = [GLKTextureLoader textureWithContentsOfFile:filePath options:options error:NULL];
//著色器
mEffect = [[GLKBaseEffect alloc]init];
//第一個(gè)紋理屬性
mEffect.texture2d0.enabled = GL_TRUE;
//紋理的名字
mEffect.texture2d0.name = textureInfo.name;
}
6.啟動(dòng)著色器并繪制
-(void)glkView:(GLKView *)view drawInRect:(CGRect)rect{
glClearColor(0.3f, 0.6f, 1.0f, 1.0f);
//清除surface內(nèi)容,恢復(fù)至初始狀態(tài)
glClear(GL_DEPTH_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
//啟動(dòng)著色器
[mEffect prepareToDraw];
glDrawArrays(GL_TRIANGLES, 0, 6);
}
5.效果圖
