圖片渲染
- 新建一個ViewController繼承自GLKViewController
- 把這個控制器設(shè)置為根視圖,view改成基礎(chǔ)GLKView
- 導(dǎo)入相應(yīng)頭文件
#import <OpenGLES/ES3/gl.h>
#import <OpenGLES/ES3/glext.h>
setUpConfig
初始化上下文&設(shè)置當(dāng)前上下文
- 使用
OpenGL做任何事前,你都需要先創(chuàng)建一個EAGLContext。 -
iOS使用OpenGL進行繪制時需要一些信息,這些信息都由EAGLContext管理。這和你使用一個Core Graphics上下文差不多。 - 當(dāng)你創(chuàng)建一個上下文時,你需要定義要使用的
API版本。
EAGLContext是蘋果iOS平臺下實現(xiàn)OpenGLES渲染層.
1. `kEAGLRenderingAPIOpenGLES1` = 1, 固定管線
2. `kEAGLRenderingAPIOpenGLES2` = 2,
3. `kEAGLRenderingAPIOpenGLES3` = 3,
context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];
設(shè)置當(dāng)前上下文
[EAGLContext setCurrentContext:context];
獲取GLKView & 設(shè)置context
- 這創(chuàng)建了一個新的
GLKView的實例,并使它和整個window一樣大。 - 當(dāng)你創(chuàng)建一個
GLKView的時候,你需要告訴它要使用的OpenGL上下文,所以配置成我們剛剛創(chuàng)建的context。
GLKView *view = (GLKView *)self.view; view.context = context;
配置視圖創(chuàng)建的渲染緩存區(qū)
- drawableColorFormat: 顏色緩存區(qū)格式
- 簡介:
OpenGL ES有一個緩存區(qū),它用以存儲將在屏幕中顯示的顏色。你可以使用其屬性來設(shè)置緩沖區(qū)中的每個像素的顏色格式。 -
GLKViewDrawableColorFormatRGBA8888:此值為默認(rèn)值,緩存區(qū)的每個像素的最小組成部分(RGBA)使用8個bit,(所以每個像素4個字節(jié),4*8個bit)。 -
GLKViewDrawableColorFormatRGB565:如果你的APP允許更小范圍的顏色,即可設(shè)置這個。會讓你的APP消耗更小的資源(內(nèi)存和處理時間)
-
drawableDepthFormat(深度緩存區(qū)格式):如果你要使用這個屬性(一般用于3D游戲),你應(yīng)該選擇GLKViewDrawableDepthFormat16或GLKViewDrawableDepthFormat24。這里的差別是使用GLKViewDrawableDepthFormat16將消耗更少的資源。
-
GLKViewDrawableDepthFormatNone= 0:意味著完全沒有深度緩沖區(qū) GLKViewDrawableDepthFormat16GLKViewDrawableDepthFormat24
view.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888;
view.drawableDepthFormat = GLKViewDrawableDepthFormat16;
設(shè)置背景顏色
定義用來清理屏幕的RGB顏色和alpha(透明度)值。這里我們設(shè)置紅色。
glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
setUpVertexData
準(zhǔn)備數(shù)據(jù)
四邊形是有兩個三角形組成.
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, //左下
};
綁定數(shù)據(jù)
創(chuàng)建頂點緩存區(qū)標(biāo)識符ID
函數(shù)原型:
void glGenBuffers(GLsizei n,GLuint * buffers);
第一個參數(shù):要生成的緩沖對象的數(shù)量
第二個參數(shù):要輸入用來存儲緩沖對象名稱的數(shù)組
如果使用數(shù)組保存
GLuint bufferID[3];
glGenBuffers(3,bufferID);
- 單個
GLuint數(shù)據(jù)
GLuint bufferID;
glGenTextures(1, &bufferID);
綁定頂點緩存區(qū)(明確作用)
函數(shù)原型:
void glBindBuffer(GLenum target,GLuint buffer);
第一個參數(shù):緩沖對象的類型
第二個參數(shù):要綁定的緩沖對象的名稱
- 使用該函數(shù)將緩沖對象綁定到
OpenGL上下文環(huán)境中以便使用。 - 如果把
target綁定到一個已經(jīng)創(chuàng)建好的緩沖對象,那么這個緩沖對象將為當(dāng)前target的激活對象。 - 但是如果綁定的
buffer值為0,那么OpenGL將不再對當(dāng)前target使用任何緩存對象。
glBindTexture(GL_ARRAY_BUFFER, bufferID);
OpenGL允許我們同時綁定多個緩沖類型,只要這些緩沖類型是不同的,換句話說,同一時間,不能綁定兩個相同類型的緩沖對象。也可以理解為對于一個類型來說,同一時間只能“激活”一個類型,否則就會發(fā)生“矛盾”。
第二個參數(shù)雖然是GLuint類型的,但是你萬萬不能直接指定個常量比如說0,否則會出現(xiàn)GL_INVALID_VALUE的錯誤,具體如下:
GL_INVALID_VALUE is generated if buffer is not a name previously returned form a call to glGenBuffers
將頂點數(shù)組的數(shù)據(jù)copy到頂點緩存區(qū)中(GPU顯存中)
glBufferData(GL_ARRAY_BUFFER, sizeof(vertexData), vertexData, GL_STATIC_DRAW);
插槽重識(打開讀取通道)
GLKit目標(biāo)就是簡化, 里面有很多對OpenGLES的封裝, 我們也可以不用進行shader的編輯, 不用在寫glsl就能完成shader的繪制
GLKVertexAttribPosition就是position位置插槽,
GLKVertexAttribColor就是color位置插槽,
GLKVertexAttribTexCoord0就是texcoord0位置插槽,
-
glEnableVertexAttribArray:在iOS中, 默認(rèn)情況下,出于性能考慮,所有頂點著色器的屬性(Attribute)變量都是關(guān)閉的。
意味著,頂點數(shù)據(jù)在著色器端(服務(wù)端)是不可用的。即使你已經(jīng)使用
glBufferData方法,將頂點數(shù)據(jù)從內(nèi)存拷貝到頂點緩存區(qū)中(GPU顯存中)。
所以, 必須由
glEnableVertexAttribArray方法打開通道。指定訪問屬性,才能讓頂點著色器能夠訪問到從CPU復(fù)制到GPU的數(shù)據(jù)。
注意: 數(shù)據(jù)在
GPU端是否可見,即,著色器能否讀取到數(shù)據(jù),由是否啟用了對應(yīng)的屬性決定,這就是glEnableVertexAttribArray的功能,允許頂點著色器讀取GPU(服務(wù)器端)數(shù)據(jù)。
- glVertexAttribPointer
函數(shù)原型:
glVertexAttribPointer (GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid* ptr)
功能:上傳頂點數(shù)據(jù)到顯存的方法(設(shè)置合適的方式從buffer里面讀取數(shù)據(jù))
參數(shù)列表:
-
index:指定要修改的頂點屬性的索引值 -
size:每次讀取數(shù)量。(如position是由3個(x,y,z)組成,而顏色是4個(r,g,b,a),紋理則是2個.) -
type:指定數(shù)組中每個組件的數(shù)據(jù)類型??捎玫姆柍A坑?code>GL_BYTE, GL_UNSIGNED_BYTE, GL_SHORT,GL_UNSIGNED_SHORT, GL_FIXED, 和GL_FLOAT,初始值為GL_FLOAT。
4.normalized:指定當(dāng)被訪問時,固定點數(shù)據(jù)值是否應(yīng)該被歸一化(GL_TRUE)或者直接轉(zhuǎn)換為固定點值(GL_FALSE) -
stride:指定連續(xù)頂點屬性之間的偏移量。如果為0,那么頂點屬性會被理解為:它們是緊密排列在一起的。初始值為0 -
ptr:指定一個指針,指向數(shù)組中第一個頂點屬性的第一個組件。初始值為0
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);
- (GLfloat *)NULL null值包裝為0
- (GLfloat *)NULL + 3 起始位置
- 乘以5直接間隔
setUpTexture
紋理矩陣
- 獲取紋理圖片路徑
NSString *filePath = [[NSBundle mainBundle]pathForResource:@"test" ofType:@"jpg"];
- 設(shè)置紋理參數(shù)
由于紋理坐標(biāo)系是跟手機顯示的Quartz 2D坐標(biāo)系的y軸正好相反,紋理坐標(biāo)系使用左下角為原點,往
上為y軸的正值,往右是x軸的正值,所以需要設(shè)置一下GLKTextureLoaderOriginBottomLeft。
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:@(1),GLKTextureLoaderOriginBottomLeft, nil];
GLKTextureInfo *textureInfo = [GLKTextureLoader textureWithContentsOfFile:filePath options:options error:nil];
- 使用蘋果
GLKit提供GLKBaseEffect完成著色器工作(頂點/片元)
eEffect = [[GLKBaseEffect alloc] init];
eEffect.texture2d0.enabled = GL_TRUE;
eEffect.texture2d0.name = textureInfo.name;
- 繪制
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect{
glClear(GL_COLOR_BUFFER_BIT);
[eEffect prepareToDraw];
glDrawArrays(GL_TRIANGLES, 0, 6);
}
效果圖

下來做一個立方體的貼圖,并旋轉(zhuǎn).
準(zhǔn)備工作
立方體和單個四邊形區(qū)別,多了
5個面,定點數(shù)也有原來的6個變成36個.
- 首先創(chuàng)建需要的類已經(jīng)一個
OPVertex來存放頂點和紋理頂點.
ypedef struct OPVertex{
GLKVector3 positionCoord;//頂點坐標(biāo)
GLKMatrix2 texturecoord; //紋理
}OPVertex;
//總共頂點數(shù)
static int const kCoordCount = 36;
EAGLContext *context; //上下文
GLKBaseEffect *cEffect;// 著色器
NSInteger angle; //記錄旋轉(zhuǎn)的角度
GLKView *glkView; //glkView
@property(nonatomic, assign) OPVertex *vertices;
繪制
- 初始化上下文&設(shè)置當(dāng)前上下文,設(shè)置渲染區(qū)和背景顏色
這一步和之前繪制沒有變化
-(void)setUpConfig
{
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ū).
view.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888;
view.drawableDepthFormat = GLKViewDrawableDepthFormat16;
glkView = view;
//4.設(shè)置背景顏色
glClearColor(1, 0, 0, 1.0);
}
- 加載頂點/紋理坐標(biāo)數(shù)據(jù)
- 使用
malloc分配內(nèi)存
self.vertices = malloc(sizeof(OPVertex) * kCoordCount);
- 6個面對應(yīng)的頂點和紋理坐標(biāo)
// 前面
self.vertices[0] = (OPVertex){{-0.5, 0.5, 0.5}, {0, 1}};
self.vertices[1] = (OPVertex){{-0.5, -0.5, 0.5}, {0, 0}};
self.vertices[2] = (OPVertex){{0.5, 0.5, 0.5}, {1, 1}};
self.vertices[3] = (OPVertex){{-0.5, -0.5, 0.5}, {0, 0}};
self.vertices[4] = (OPVertex){{0.5, 0.5, 0.5}, {1, 1}};
self.vertices[5] = (OPVertex){{0.5, -0.5, 0.5}, {1, 0}};
// 上面
self.vertices[6] = (OPVertex){{0.5, 0.5, 0.5}, {1, 1}};
self.vertices[7] = (OPVertex){{-0.5, 0.5, 0.5}, {0, 1}};
self.vertices[8] = (OPVertex){{0.5, 0.5, -0.5}, {1, 0}};
self.vertices[9] = (OPVertex){{-0.5, 0.5, 0.5}, {0, 1}};
self.vertices[10] = (OPVertex){{0.5, 0.5, -0.5}, {1, 0}};
self.vertices[11] = (OPVertex){{-0.5, 0.5, -0.5}, {0, 0}};
// 下面
self.vertices[12] = (OPVertex){{0.5, -0.5, 0.5}, {1, 1}};
self.vertices[13] = (OPVertex){{-0.5, -0.5, 0.5}, {0, 1}};
self.vertices[14] = (OPVertex){{0.5, -0.5, -0.5}, {1, 0}};
self.vertices[15] = (OPVertex){{-0.5, -0.5, 0.5}, {0, 1}};
self.vertices[16] = (OPVertex){{0.5, -0.5, -0.5}, {1, 0}};
self.vertices[17] = (OPVertex){{-0.5, -0.5, -0.5}, {0, 0}};
// 左面
self.vertices[18] = (OPVertex){{-0.5, 0.5, 0.5}, {1, 1}};
self.vertices[19] = (OPVertex){{-0.5, -0.5, 0.5}, {0, 1}};
self.vertices[20] = (OPVertex){{-0.5, 0.5, -0.5}, {1, 0}};
self.vertices[21] = (OPVertex){{-0.5, -0.5, 0.5}, {0, 1}};
self.vertices[22] = (OPVertex){{-0.5, 0.5, -0.5}, {1, 0}};
self.vertices[23] = (OPVertex){{-0.5, -0.5, -0.5}, {0, 0}};
// 右面
self.vertices[24] = (OPVertex){{0.5, 0.5, 0.5}, {1, 1}};
self.vertices[25] = (OPVertex){{0.5, -0.5, 0.5}, {0, 1}};
self.vertices[26] = (OPVertex){{0.5, 0.5, -0.5}, {1, 0}};
self.vertices[27] = (OPVertex){{0.5, -0.5, 0.5}, {0, 1}};
self.vertices[28] = (OPVertex){{0.5, 0.5, -0.5}, {1, 0}};
self.vertices[29] = (OPVertex){{0.5, -0.5, -0.5}, {0, 0}};
// 后面
self.vertices[30] = (OPVertex){{-0.5, 0.5, -0.5}, {0, 1}};
self.vertices[31] = (OPVertex){{-0.5, -0.5, -0.5}, {0, 0}};
self.vertices[32] = (OPVertex){{0.5, 0.5, -0.5}, {1, 1}};
self.vertices[33] = (OPVertex){{-0.5, -0.5, -0.5}, {0, 0}};
self.vertices[34] = (OPVertex){{0.5, 0.5, -0.5}, {1, 1}};
self.vertices[35] = (OPVertex){{0.5, -0.5, -0.5}, {1, 0}};
- 開辟頂點緩存區(qū)
GLuint bufferID;
glGenBuffers(1, &bufferID);
//(2).綁定頂點緩存區(qū).(明確作用)
glBindBuffer(GL_ARRAY_BUFFER, bufferID);
//(3).將頂點數(shù)組的數(shù)據(jù)copy到頂點緩存區(qū)中(GPU顯存中)
glBufferData(GL_ARRAY_BUFFER, sizeof(OPVertex) * kCoordCount, self.vertices, GL_STATIC_DRAW);
// 頂點坐標(biāo)數(shù)據(jù)
glEnableVertexAttribArray(GLKVertexAttribPosition);
glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, sizeof(OPVertex) , offsetof(OPVertex, positionCoord) + NULL);
//紋理坐標(biāo)數(shù)據(jù)
glEnableVertexAttribArray(GLKVertexAttribTexCoord0);
glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(OPVertex), offsetof(OPVertex, texturecoord) + NULL);
sizeof(OPVertex) * kCoordCount傳入內(nèi)容的大小
offsetof(OPVertex, positionCoord) + NULL,對應(yīng)頂點或者紋理起始點的內(nèi)存偏移值
- 加載紋理數(shù)據(jù)
{
//1.獲取紋理圖片路徑
NSString *filePath = [[NSBundle mainBundle]pathForResource:@"test" ofType:@"jpg"];
//2.設(shè)置紋理參數(shù)
//紋理坐標(biāo)原點是左下角,但是圖片顯示原點應(yīng)該是左上角.
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;
cEffect.texture2d0.target = textureInfo.target;
[self setTimer];
}
cEffect.texture2d0.target = textureInfo.target;將目標(biāo)對象交給cEffect;
[self setTimer];開啟一個定時器
- 開始定時器
- (void)setTimer{
NSTimer* timer = [NSTimer timerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
[self transforms];
}];
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
}
- (void)transforms{
angle = (angle + 10) % 360;
//使用模型矩陣
cEffect.transform.modelviewMatrix = GLKMatrix4MakeRotation(GLKMathDegreesToRadians(angle), 0.3, 1, -1.7);
//重新繪制
[glkView display];
}
- 繪制
#pragma mark -- GLKViewDelegate
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
{
//1.清屏
glClear(GL_COLOR_BUFFER_BIT);
//2.準(zhǔn)備繪制
[cEffect prepareToDraw];
//3.開始繪制
glDrawArrays(GL_TRIANGLES, 0, 36);
}
這里
36繪制的定點數(shù).
效果圖:

總結(jié)
-
OpenGL ES 相關(guān)初始化
- 初始化上下文&設(shè)置當(dāng)前上下文
- 獲取GLKView & 設(shè)置context
- 配置視圖創(chuàng)建的渲染緩存區(qū)
- 設(shè)置背景顏色
-
加載頂點/紋理坐標(biāo)數(shù)據(jù)
- 設(shè)置頂點數(shù)組(頂點坐標(biāo),紋理坐標(biāo))
- 開辟頂點緩存區(qū)
- 創(chuàng)建頂點緩存區(qū)標(biāo)識符ID
- 綁定頂點緩存區(qū).(明確作用)
- 將頂點數(shù)組的數(shù)據(jù)copy到頂點緩存區(qū)中(GPU顯存中)
- 打開讀取通道.
- 上傳頂點數(shù)據(jù)到顯存的方法
-
加載紋理數(shù)據(jù)(使用GLBaseEffect)
- 獲取紋理圖片路徑
- 設(shè)置紋理參數(shù)
- 使用蘋果GLKit 提供GLKBaseEffect 完成著色器工作(頂點/片元)
-
繪制視圖的內(nèi)容(GLKViewDelegate)
- 清屏
- 準(zhǔn)備繪制
- 開始繪制
GLKBaseEffect使用小結(jié):
- 創(chuàng)建一個
GLKBaseEffect。通常當(dāng)你創(chuàng)建OpenGL上下文的時候,你都會創(chuàng)建一個。對于不同的幾何圖形,你可以(或者說應(yīng)該)重用相同的GLKBaseEffect,只是要重新設(shè)置其屬性。在后臺,GLKBaseEffect只會向它的著色器傳播剛剛變化的屬性。 - 設(shè)置
GLKBaseEffect的屬性。這里你可以配置光,轉(zhuǎn)化,和其他GLKBaseEffect的著色器用來渲染幾何圖形的屬性。 - 調(diào)用G
LKBaseEffect的prepareToDraw方法。任何時候如果你改變了GLKBaseEffect的一個屬性,你都要先調(diào)用prepareToDraw方法讓著色器得到正確的配置。這也讓GLKBaseEffect的著色器作為“當(dāng)前”的著色器程序(就是讓GLKBaseEffect的著色器起作用)。 - 使能預(yù)定義屬性。通常當(dāng)你使用自定義著色器時,他們會使用稱為屬性的參數(shù),你要寫代碼獲得它們的
ID。對于GLKBaseEffect的內(nèi)建著色器,這些屬性已經(jīng)用常量值預(yù)先定義好了,比如GLKVertexAttribPosition或者GLKVertexAttribColor。所以你要使能所有需要傳給著色器的參數(shù),并保證他們指向了數(shù)據(jù)。 - 繪制你的幾何圖形。一旦你東西要配置,你都可以使用普通的
OpenGL繪圖指令,比如glDrawArrays或者glDrawElements,它會使用你剛剛配置好的效果進行渲染!
GLKBaseEffect的好處是如果你使用它們,你根本就不需要去寫任何的著色器,