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);
}
一下函数的解析:
此demo对落影大神的基础上加上更多的注释和体会。