介绍过GLKit常用AP之后,接下来通过本案例熟悉GLKit框架的使用。使用GLKit完成如下效果。
创建工程
- 创建一个iOS项目工程,将ViewController的父类修改为GLKViewController,并添加相关的头文件。
#import <OpenGLES/ES3/gl.h>
#import <OpenGLES/ES3/glext.h>
@interface ViewController : GLKViewController
-
在main.storyboard中,修改view的父类为GLKView。
image.png
创建全局变量
使用GLKit时,需要使用两个全局变量
@interface ViewController (){
//上下文,状态机
EAGLContext *context;
//光照/着色系统
GLKBaseEffect *cEffect;
}
OpenGL ES 相关初始化
- 初始化上下文
context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];
if(!context){
NSLog(@"Create ES context Failed");
return;
}
- 设置当前上下文
[EAGLContext setCurrentContext:context];
- 获取当前的view(GLKView类型)并将view的context设置步骤1初始化的context。
GLKView* view = (GLKView*)self.view;
view.context = context;
- 配置颜色缓冲区和深度缓冲区的格式
/*
drawableColorFormat:配置颜色缓冲区格式,共有3种格式,默认为GLKViewDrawableColorFormatRGBA8888
GLKViewDrawableColorFormatRGBA8888:表示RGBA各占8bit
GLKViewDrawableColorFormatRGB565:如果App允许更小范围的颜色,可以设置为这个值,可以让App消耗更少的资源
GLKViewDrawableColorFormatSRGBA8888:一种sRGBA8888格式
*/
view.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888;
/*
drawableDepthFormat:深度缓冲区格式也有3种格式,默认为GLKViewDrawableDepthFormat24
GLKViewDrawableDepthFormatNone:没有深度缓冲区
GLKViewDrawableDepthFormat16:一般用于3D游戏,相比24而言,消耗更少的资源
GLKViewDrawableDepthFormat24:一般用于3D游戏
*/
view.drawableDepthFormat = GLKViewDrawableDepthFormat16;
- 设置背景颜色
glClearColor(1, 0, 0, 1.0);
加载顶点/纹理坐标数据
- 设置顶点数组(顶点坐标,纹理坐标)
/*
纹理坐标系取值范围[0,1];原点是左下角(0,0);
故而(0,0)是纹理图像的左下角, 点(1,1)是右上角.
*/
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, //左下
};
- 开辟顶点缓冲区
- 创建顶点缓冲区标识符ID
GLuint bufferID;
glGenBuffers(1, &bufferID);
- 绑定顶点缓冲区(明确作用)
glBindBuffer(GL_ARRAY_BUFFER, bufferID);
- 将顶点数组的数据copy到顶点缓冲区中(GPU显存中)
glBufferData(GL_ARRAY_BUFFER, sizeof(vertexData), vertexData, GL_STATIC_DRAW);
- 打开读取通道
在iOS中,默认情况下出于性能考虑,所有顶点着色器的属性(Attribute)变量都是关闭的。这意味着,顶点数据在着色器端(服务端)是不可以用的。即使你已经使用glBufferData方法,将顶点数据从内存copy到顶点缓冲区(GPU显存)中。
这个时候必须由glEnableVertexAttribArray方法打开通道,指定要访问的属性,才能让顶点着色器访问到从CPU复制到GPU的数据。
注意:数据在GPU端是否可见,即着色器能否读取到数据,由是否开启了对应的属性决定。这就是glEnableVertexAttribArray的功能,允许顶点着色器读GPU数据。
//顶点坐标数据
//打开顶点坐标属性通道
glEnableVertexAttribArray(GLKVertexAttribPosition);
/*
上传顶点数据到显存的方法(设置合适的方式从buffer里面读取数据)
index:指定需要修改顶点属性的索引值
size:每次读取的数量,如position是由3个(x,y,z)组成,而颜色是4个(r,g,b,a),纹理则是2个.
type:指定数组中每个组件的数据类型,可用的符号常量有GL_BYTE, GL_UNSIGNED_BYTE, GL_SHORT,GL_UNSIGNED_SHORT, GL_FIXED, 和 GL_FLOAT,初始值为GL_FLOAT
normalized:指定当被访问时,固定点数据是否应该被归一化(GL_TRUE)或者直接转换为固定点的值(GL_FALSE)
stride:指定连续顶点属性之间的偏移量,如果为0,那顶点属性会被理解为:它们是紧密排列在一起的。初始值为0
ptr:指定一个指针,指向数组中第一个顶点属性的第一个组件,初始为0
*/
//void glVertexAttribPointer (GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid* ptr);
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);
加载纹理数据(使用GLBaseEffect)
- 获取纹理图片路径
NSString *filePath = [[NSBundle mainBundle]pathForResource:@"nn" ofType:@"jpg"];
- 设置纹理参数
纹理坐标的原点是左下角,但图片显示原点的位置在左上角,此处应该修改图片显示原点的位置为左下角。
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:@(1),GLKTextureLoaderOriginBottomLeft, nil];
- 从纹理文件中读取纹理信息
GLKTextureInfo *textureInfo = [GLKTextureLoader textureWithContentsOfFile:filePath options:options error:nil];
- 使用苹果GLKit 提供GLKBaseEffect 完成着色器工作(顶点/片元)
cEffect = [[GLKBaseEffect alloc] init];
cEffect.texture2d0.enabled = GL_TRUE;
cEffect.texture2d0.name = textureInfo.name;
在GLKView代理方法中将纹理绘制到屏幕上
注意:代理方法必须实现,否则无法实现绘制
- 清除缓冲区
glClear(GL_COLOR_BUFFER_BIT);
- 准备绘制
[cEffect prepareToDraw];
- 开始绘制
glDrawArrays(GL_TRIANGLES, 0, 6);
至此完成了使用GLKit框架进行纹理绘制。
完整Demo
网友评论