初识GLKit

GLKit 框架是为了简化iOS上OpenGL ES的开发,提供的基于OpenGL ES的iOS框架。

实现思路:

1. 新建OpenGLES 上下文 并且配置环境
2. 设置顶点数据数据和设置缓存
3. 创建着色器效果,并启动着色器

注意:

1. ViewController要继承GLKViewController
2. 继承GLKViewController后要实现 - (void)glkView:(GLKView *)view drawInRect:(CGRect)rect 方法

一些方法的解析:

GLKBaseEffect
Effect效果类提供标准的公共着色效果的实现。能够配置效果和相关的顶点数据,然后创建和加载适当的着色器。GLKit 包括三个可配置着色效果类:GLKBaseEffect实现OpenGL ES 1.1规范中的关键的灯光和材料模式, GLKSkyboxEffect提供一个skybox效果的实现, GLKReflectionMapEffect 在GLKBaseEffect基础上包括反射映射支持。
为什么OpenGL中要用GLfloat而不用float,两者有什么区别呢? 
不同的机器上 float的占用的字节大小不同,
比如有的机器上 float 是4个字节 
有的机器 float 是8个字节 
而GLfloat 则是在不同的环境下对应不同的处理
以确保字节数相等
GLuint
GLuint  就是正整形, 和C里面的unsigned int 一样。
Vertex Buffer Object (VBO)
创建VBO需要3个步骤:
使用glGenBuffers() 请求OpenGL ES 为图形处理器控制的缓存书城一个独一无二的标识符。
使用glBindBuffer()  告诉OpenGL ES为接下来的运算使用一个缓存。
使用glBufferData() 让OpenGL ES 为当前捆绑的缓存分配并初始化足够的连续内存。
void glGenBuffersARB(GLsizei n, GLuint* ids)
glGenBuffersARB() 创建(一个或多个)缓冲区对象并返回这些对象的标示符。该函数包括两个参数:第一个是欲创建缓冲区对象的个数,第二个是用于存储(一个或多个)缓冲区标示符的数组地址(标示符为GLuint类型)

void glBindBufferARB(GLenum target, GLuint id)
一旦缓冲区对象被建立,在使用它之前我们需要使用该函数将其与一个真实的缓冲区空间绑定。glBindBufferARB()有两个参数:target和ID。

Target作为一个标示符,告诉VBO该缓冲区对象是存储顶点数组数据(GL_ARRAY_BUFFER_ARB)还是索引数据(GL_ELEMENT_ARRAY_BUFFER_ARB)。
任何顶点的属性,包括顶点坐标、纹理坐标、法向量和颜色分量数组都需要使用GL_ARRAY_BUFFER_ARB。在glDrawElements()或glRangeElements()里会用到的索引数组此处必须使用GL_ELEMENT_ARRAY_BUFFER_ARB。注意到这里的target实际上协助VBO决定了存储相应缓冲区对象的最高效区域,比如某些系统会将索引存储在AGP或者系统内存中,而顶点会存储在显存中。

一旦glBindBufferARB()被首次调用,VBO会首先在内存中赋予一个大小为0的缓冲区空间,并随后初始化VBO状态,比如用途和可操作性。


void glBufferDataARB(GLenum target, GLsizei size, const void* data, GLenum usage)

当缓冲区对象被初始化后,你可以使用glBufferDataARB将数据拷贝进缓冲区对象。该函数有四个参量。与前一个函数相同,第一个参量只能从GL_ARRAY_BUFFER_ARB或者GL_ELEMENT_ARRAY_BUFFER_ARB中选择。Size代表欲复制数据的总字节数。第三个参量是指向该源数据数组的指针;如果该指针为NULL,则VBO会在内存中开辟出一块size大小的空间。最后一个参量usage是另一个传递给VBO的标示符,用来决定该缓冲区对象如何使用:static, dynamic 还是 stream,和read, copy 和 draw。

VBO允许usage标示符取以下9种值:

GL_STATIC_DRAW_ARB

GL_STATIC_READ_ARB

GL_STATIC_COPY_ARB

GL_DYNAMIC_DRAW_ARB

GL_DYNAMIC_READ_ARB

GL_DYNAMIC_COPY_ARB

GL_STREAM_DRAW_ARB

GL_STREAM_READ_ARB

GL_STREAM_COPY_ARB

"Static”意味着VBO中的数据不会被改变(一次修改,多次使用),"dynamic”意味着数据可以被频繁修改(多次修改,多次使用),"stream”意味着数据每帧都不同(一次修改,一次使用)。"Draw”意味着数据将会被送往GPU进行绘制,"read”意味着数据会被用户的应用读取,"copy”意味着数据会被用于绘制和读取。注意在使用VBO时,只有draw是有效的,而copy和read主要将会在像素缓冲区(PBO)和帧缓冲区(FBO)中发挥作用。

系统会根据usage标示符为缓冲区对象分配最佳的存储位置,比如系统会为GL_STATIC_DRAW_ARB和GL_STREAM_DRAW_ARB分配显存,GL_DYNAMIC_DRAW_ARB分配AGP,以及任何_READ_相关的缓冲区对象都会被存储到系统或者AGP中因为这样数据更容易读写。
sizeof()
在 Pascal 语言中,sizeof() 是一种内存容量度量函数,功能是返回一个变量或者类型的大小(以字节为单位);在 C 语言中,sizeof() 是一个判断数据类型或者表达式长度的运算符。
在Pascal 语言与C语言中,对 sizeof() 的处理都是在编译阶段进行。
glVertexAttribPointer()
glVertexAttribPointer(<#GLuint indx#>, <#GLint size#>, <#GLenum type#>, <#GLboolean normalized#>, <#GLsizei stride#>, <#const GLvoid *ptr#>)

//传递数据
    //1:传递是什么数据?这里是顶点位置数据GLKVertexAttribPosition
    //2:数据的大小(每个点的数据个数,我们每个点只有xy2个值所以是2,如果做三维的话还有z坐标,那么就是3)
    //3:顶点的数据类型,我们定义的是GLfloat数组所以是GL_FLOAT,如果用int定义顶点那么就是GL_INT.。。
    //4:这个。。一般都是GL_FALSE
    //5:跨度值,简单来讲,就是隔多少个值取一个点。假如设为1(这个1不是真正的1,因为跨度是按内存空间来算的),
    //就是1个单位,那么我们就只能取到1,3,5.。。24被跨过去了。这个在使用顶点结构体的时候会有用,现在设为0
    //6:数据的地址,就是数组的名字 (这个解析是错的)
glDrawArrays

该方法原型:
glDrawArrays(int mode, int first,int count)
参数1:有三种取值
1.GL_TRIANGLES:每三个顶之间绘制三角形,之间不连接
2.GL_TRIANGLE_FAN:以V0V1V2,V0V2V3,V0V3V4,……的形式绘制三角形
3.GL_TRIANGLE_STRIP:顺序在每三个顶点之间均绘制三角形。这个方法可以保证从相同的方向上所有三角形均被绘制。以V0V1V2,V1V2V3,V2V3V4……的形式绘制三角形
参数2:从数组缓存中的哪一位开始绘制,一般都定义为0
参数3:顶点的数量

主要代码如下:

- (void)viewDidLoad {
    
    [super viewDidLoad];
    
    [self initContext];
    [self initImageVertexAndBuffer];
    [self initTexture];
    
    
}

- (void)initContext {
    //新建OpenGLES 上下文 并且配置环境
    self.mContext = [[EAGLContext alloc]initWithAPI:kEAGLRenderingAPIOpenGLES2];
    
    GLKView * view = (GLKView *)self.view;
    view.context = self.mContext;
    view.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888; //颜色缓冲区格式
    
    [EAGLContext setCurrentContext:self.mContext];
    
}


- (void)initImageVertexAndBuffer {
    
    //顶点数组,前三个是顶点坐标,后面两个是纹理坐标
    GLfloat imageVertexArray[] = {
        
        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, //左下
        
    };
    
    //顶点数据缓存
    GLuint buffer;
    //创建一个缓冲区
    glGenBuffers(1, &buffer);
    //绑定
    glBindBuffer(GL_ARRAY_BUFFER, buffer);
    //初始化并为缓存分配内存
    glBufferData(GL_ARRAY_BUFFER, sizeof(imageVertexArray), imageVertexArray, GL_STATIC_DRAW);
    
    //顶点缓存
    //glEnableVertexAttribArray() 告诉OpenGL ES 接下来的渲染中是否使用缓存中的数据;
    glEnableVertexAttribArray(GLKVertexAttribPosition);

//glVertexAttribPointer() 告诉OpenGL ES 在缓存中的数据类型和所有需要访问的数据内存偏移值
    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);
    
}

- (void)initTexture {
    
    NSString * filePath = [[NSBundle mainBundle] pathForResource:@"for_test" ofType:@"jpg"];
    
    //GLKTextureLoaderOriginBottomLeft 纹理坐标系是相反的
    NSDictionary * options = [NSDictionary dictionaryWithObjectsAndKeys:@(1),GLKTextureLoaderOriginBottomLeft, nil];
    
    //GLKTextureLoader读取图片,创建纹理GLKTextureInfo
    GLKTextureInfo * tureInfo = [GLKTextureLoader textureWithContentsOfFile:filePath options:options error:nil];
    
    self.mEffect = [[GLKBaseEffect alloc]init];
    self.mEffect.texture2d0.enabled = GL_TRUE;
    self.mEffect.texture2d0.name = tureInfo.name;
    
}


- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect {
    
    //定义清除屏幕的颜色(即屏幕的背景色,因为屏幕可能残留其他数据,不清屏的话说不定看到花瓶哦)
    glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
    
    //上面只是定义了要用什么颜色清理屏幕,这里才是真正的扫地工人!
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
    //启动着色器
    [self.mEffect prepareToDraw];
    
    glDrawArrays(GL_TRIANGLES, 0, 6);
    
}

一下函数的解析:

image.png

此demo对落影大神的基础上加上更多的注释和体会。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270