1. 框架概述
GLKit 框架的设计目的是为了简化基于 OpenGL/OpenGL ES 的应用开发。它的出现加快了 OpenGL/OpenGL ES 应用程序的开发。使用数学库,背景纹理加载,预先创建的着色器效果以及标准视图和视图控制器来实现渲染循环。
GLKit 框架提供了功能和类,可以减少创建新的基于着色器的应用程序所需的工作量,或者支持依赖早期版本的 OpenGL/OpenGL ES 提供的固定函数顶点或片段处理的现有应用程序。
GLKView: 提供绘制场所(View) GLKViewController: 扩展于标准的 UIKit 设计模式,用于绘制视图内容的管理与呈现
2. GLKit 功能
- 加载纹理
- 提供高性能的数学运算
- 提供常见的着色器
- 提供视图及视图控制器
3. GLKit 纹理加载
3.1 GLKTextureInfo 属性
-
GLuint name: OpenGL 上下文中纹理名称 -
GLenum target: 纹理绑定的目标 -
GLuint width: 加载的纹理的宽度 -
GLuint height: 加载的纹理的高度 -
GLuint depth: 加载的纹理的深度值 -
GLKTextureInfoAlphaState alphaState: 加载的纹理的透明度分量状态 -
GLKTextureInfoOrigin textureOrigin: 加载的纹理中的原点位置 -
BOOL containsMipmaps: 加载的纹理是否包含 mipmap -
GLuint mimapLevelCount: mipmap 的 level 层级
3.1 GLKTextureInfo 初始化
-(instancetype)initWithSharegroup:(EAGLSharegroup *)sharegroup
-(instancetype)initWithShareContext:(NSOpenGLContext *)context
3.2 使用 GLKTextureLoader 加载纹理
3.2.1 同步加载纹理
-
从文件加载 2D 纹理图像并从数据中创建新的纹理
+ (nullable GLKTextureInfo *)textureWithContentsOfFile:(NSString *)path options:(nullable NSDictionary<NSString*, NSNumber*> *)options error:(NSError * __nullable * __nullable)outError -
从 URL 加载 2D 纹理图像并从数据中创建新的纹理
+ (nullable GLKTextureInfo *)textureWithContentsOfURL:(NSURL *)url options:(nullable NSDictionary<NSString*, NSNumber*> *)options error:(NSError * __nullable * __nullable)outError -
从内存空间加载 2D 纹理图像并从数据中创建新的纹理
+ (nullable GLKTextureInfo *)textureWithContentsOfData:(NSData *)data options:(nullable NSDictionary<NSString*, NSNumber*> *)options error:(NSError * __nullable * __nullable)outError -
从 Quartz 图像加载 2D 纹理图像并从数据中创建新的纹理
+ (nullable GLKTextureInfo *)textureWithCGImage:(CGImageRef)cgImage options:(nullable NSDictionary<NSString*, NSNumber*> *)options error:(NSError * __nullable * __nullable)outError
3.2.2 异步加载纹理
-
从文件中异步加载 2D 纹理图像并从数据中创建新的纹理
- (void)textureWithContentsOfFile:(NSString *)path options:(nullable NSDictionary<NSString*, NSNumber*> *)options queue:(nullable dispatch_queue_t)queue completionHandler:(GLKTextureLoaderCallback)block -
从 URL 异步加载 2D 纹理图像并从数据中创建新的纹理
- (void)textureWithContentsOfURL:(NSURL *)url options:(nullable NSDictionary<NSString*, NSNumber*> *)options queue:(nullable dispatch_queue_t)queue completionHandler:(GLKTextureLoaderCallback)block -
从内存空间异步加载 2D 纹理图像并从数据中创建新的纹理
- (void)textureWithContentsOfData:(NSData *)data options:(nullable NSDictionary<NSString*, NSNumber*> *)options queue:(nullable dispatch_queue_t)queue completionHandler:(GLKTextureLoaderCallback)block -
从 Quartz 图像异步加载 2D 纹理图像并从数据中创建新的纹理
- (void)textureWithCGImage:(CGImageRef)cgImage options:(nullable NSDictionary<NSString*, NSNumber*> *)options queue:(nullable dispatch_queue_t)queue completionHandler:(GLKTextureLoaderCallback)block
4. GLKView
4.1 GLKView 属性
-
id <GLKViewDelegate> delegate: 视图代理对象 -
EAGLContext *context: 绑定的上下文,记录 OpenGL 的状态 -
NSInteger drawableWidth: 底层缓存区对象的宽度(以像素为单位) -
NSInteger drawableHeight: 底层缓存区对象的高度(以像素为单位) -
GLKViewDrawableColorFormat drawableColorFormat: 颜色渲染缓冲区的格式- GLKViewDrawableColorFormatRGBA8888
- GLKViewDrawableColorFormatRGB565
- GLKViewDrawableColorFormatSRGBA8888
一般我们使用
GLKViewDrawableColorFormatRGBA8888, 表示颜色值的 RGBA 的每一个分别占 8 位 -
GLKViewDrawableDepthFormat drawableDepthFormat: 深度渲染缓冲区的格式- GLKViewDrawableDepthFormatNone: 不使用深度测试
- GLKViewDrawableDepthFormat16: 深度测试计算精度 16, 一般业务场景使用
- GLKViewDrawableDepthFormat24: 深度测试计算精度 24, 3D游戏业务场景使用
-
GLKViewDrawableStencilFormat drawableStencilFormat: 模板渲染缓冲区的格式- GLKViewDrawableStencilFormatNone: 不使用模板缓冲区
- GLKViewDrawableStencilFormat8: 启用模板缓冲区
-
GLKViewDrawableMultisample drawableMultisample: 多重采样缓冲区的格式- GLKViewDrawableMultisampleNone: 不使用多重采样缓冲区
- GLKViewDrawableMultisample4X: 启用多重采样缓冲区
-
UIImage *snapshot: 返回一个当前绘制的内容的截图UIImage对象 -
BOOL enableSetNeedsDisplay: 是否响应UIView的setNeedsDisplay方法,如果设为YES,那么就会像UIView一样,如果当前视图正在显示,当下一次绘制循环的时候就会再调用一次drawRect方法,否则就不会调用drawRect方法。
4.2 GLKView 初始化
GLKView 的初始化除了指定 frame 以外还比须指定一个上下文context
- (instancetype)initWithFrame:(CGRect)frame context:(EAGLContext *)context
4.3 GLKView 方法
-
- (void)bindDrawable: 将底层的 frameBuffer 对象绑定到 OpenGL ES -
- (void)deleteDrawable: 删除与视图绑定的可绘制对象,一般需要开发人员在dealloc方法里面调用 -
- (void)display: 立即重绘视图内容,类似于OpenGL里面的glutPostRedisplay()方法 -
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect: 代理方法,必须实现,一般在这里写绘图的代码
5. GLKViewController
5.1 GLKViewController 属性
-
id <GLKViewControllerDelegate> delegate: 代理对象 -
NSInteger preferredFramesPerSecond: 每秒图像帧数量,默认每秒 30 帧 -
NSInteger framesPerSecond: 实际渲染每秒有多少帧 -
BOOL paused: 用于暂停或者开启控制器的渲染 -
BOOL pauseOnWillResignActive: 是否在控制器即将进入后台暂停渲染循环 -
BOOL resumeOnDidBecomeActive: 是否在控制器已经进入前台开始渲染循环 -
NSInteger framesDisplayed: 从绘制开始到目前为止总共渲染了多少帧 -
NSTimeInterval timeSinceFirstResume: 自视图控制器第一次恢复发送更新事件以来经过的时间量 -
NSTimeInterval timeSinceLastResume: 自上次视图控制器恢复发送更新事件以来经过的时间量 -
NSTimeInterval timeSinceLastUpdate: 自上次视图控制器调用代理方法glkViewControllerUpdate已经经过的时间量 -
NSTimeInterval timeSinceLastDraw: 自上次视图控制器调用GLKView的display()方法以来经过的时间量
5.2 GLKViewController 方法
5.3 GLKViewControllerDelegate 方法
-
处理更新事件,在显示每个帧之前调用(必须实现)
- (void)glkViewControllerUpdate:(GLKViewController *)controller -
暂停/恢复通知,在渲染循环暂停或恢复之前调用(可选实现)
- (void)glkViewController:(GLKViewController *)controller willPause:(BOOL)pause
6. GLKBaseEffect
GLKBaseEffect 是 Apple 在 GLKit 里面封装的一个工具类,这个工具类可以完成一些着色器的工作,但是不等同于 OpenGL 里面的 shaderManager。
6.1 GLKBaseEffect 属性
-
GLboolean colorMaterialEnabled: 表示是否计算光照与材质交互时是否使用颜色顶点属性,默认值为GL_FALSE -
GLboolean lightModelTwoSided: 表示是否为基元的两侧计算光照,默认值为GL_FALSE -
GLboolean useConstantColor: 使用使用颜色常量,默认值为GL_TRUE -
GLKEffectPropertyTransform *transform: 绑定效果时应用于顶点数据的模型视图,投影和纹理变换的矩阵,默认值单元矩阵Identity Matrices -
GLKEffectPropertyLight *light0, *light1, *light2: 场景中的第一个光照属性,第二个光照属,第三个光照属。默认不使用光照 -
GLKLightingType lightingType: 用于计算每个片元的光照策略,默认为:GLKLightingTypePerVertexGLKLightingTypePerVertex:表示在三角形中每个顶点执行光照计算,然后再三角形进行插值GLKLightingTypePerPixel:表示光照计算的输入在三角形内插值,并且在每个片元执行光照计算
-
GLKVector4 lightModelAmbientColor: 环境颜色,应用于效果渲染的所有图元,默认为{ 0.2, 0.2, 0.2, 1.0 } -
GLKEffectPropertyMaterial *material: 计算渲染图元光照使用的材质属性,默认值为默认的材料属性:Default material state -
GLKEffectPropertyTexture *texture2d0, *texture2d1: 第一个属性纹理和第二个属性纹理,GLKBaseEffect最多支持使用两个纹理 -
NSArray<GLKEffectPropertyTexture*> *textureOrder: 纹理应用与渲染图元的顺序,默认为:texture2d0,texture2d1 -
GLKVector4 constantColor: 颜色常量,默认黑色:{ 1.0, 1.0, 1.0, 1.0 } -
GLKEffectPropertyFog *fog: 应用与场景的雾属性,默认不启用 -
NSString *label: 名称,类似于创建dispatch_queue_create()函数创建队列的标签,默认为@"GLKBaseEffect"
6.2 GLKBaseEffect 方法
准备绘制 - (void) prepareToDraw
7. GLKit 加载图片案例分享
7.1 公共部分初始化代码
- (void)setupConfig
{
// 初始化上下文
context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];
if (context == nil) {
NSLog(@"create openGL ES context failed!"); return;
}
// 设置为当前上下文
[EAGLContext setCurrentContext:context];
// 获取 GLKView
GLKView *glView = (GLKView *)self.view;
[glView setContext:context];
glView.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888;
glView.drawableDepthFormat = GLKViewDrawableDepthFormat24;
// 设置背景色
glClearColor(1.0, 0.7, 0.2, 1.0);
}
7.2 加载顶点数据
- (void)setupVertetData
{
// 1. 顶点数据, 纹理坐标
// 把顶点数据和纹理坐标放在同一个数组里
GLfloat vertexData[] = {
//x y z s t
0.5, -0.5, 0.0, 1.0, 0.0, // 右下角
0.5, 0.5, 0.0, 1.0, 1.0, // 右上角
-0.5, 0.5, 0.0, 0.0, 1.0, // 左上角
0.5, -0.5, 0.0, 1.0, 0.0, // 右下角
-0.5, 0.5, 0.0, 0.0, 1.0, // 左上角
-0.5, -0.5, 0.0, 0.0, 0.0 // 左下角
};
// 2.将上面的顶点数据从内存 copy 到显存中, 是的 openGL ES 的数据访问更高效
// 2.1 创建顶点缓冲区标识符 id
GLuint bufferID;
glGenBuffers(1, &bufferID);
// 2.2 绑定缓存区
glBindBuffer(GL_ARRAY_BUFFER, bufferID);
// 2.3 将顶点数据复制到顶点缓冲区里面
glBufferData(GL_ARRAY_BUFFER, sizeof(vertexData), vertexData, GL_STATIC_DRAW);
// 3.打开存储顶点坐标数据的 attributed 通道
/*
注意: iOS 默认情况下,出于性能的考虑所有顶点属性(attribute)通道是关闭的,不管你使用 GLKit 和 GLSL 都是关闭的, 所以需要我们自己手动打开
需要用到什么就指定哪一个 index 即可 glEnableVertexAttribArray (GLuint index)
GLKVertexAttribPosition // 顶点
GLKVertexAttribNormal, // 法线
GLKVertexAttribColor, // 颜色值
GLKVertexAttribTexCoord0, // 纹理 0 (只有两个纹理, 因为 GLKit 的 GLKBaseEffect 只支持两个纹理)
GLKVertexAttribTexCoord1 // 纹理 1 (只有两个纹理, 因为 GLKit 的 GLKBaseEffect 只支持两个纹理)
*/
glEnableVertexAttribArray(GLKVertexAttribPosition); // 打开顶点坐标 attributed 通道
glEnableVertexAttribArray(GLKVertexAttribTexCoord0);// 打开纹理坐标 attributed 通道
// 4. 告知 OpenGL 读取数据的格式 (前三个为顶点坐标, 后面两个是纹理坐标)
/*
glVertexAttribPointer(GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid *ptr)
参数 1: indx, 读取对应数据类型的索引
参数 2: size, 读取该类型数据的步长
参数 3: type, 该数据的数据类型
参数 4: normalized 是否需要归一化, 一般为不需要 GL_FALSE
参数 5: stride 第一次数据读取和第二次数据读取的间隔
参数 6: ptr 读取数据的地址
所谓归一化其实就是把数据转为标准格式, 范围转为[-1, 1]
*/
// 4.1 顶点坐标读取方式
/*
参数 1: indx, 读取对应数据类型的索引: GLKVertexAttribPosition
参数 2: size, 读取该类型数据的步长: 3 (顶点数据坐标有三个 x, y, z)
参数 3: type, 该数据的数据类型: GL_FLOAT 类型
参数 4: normalized 是否需要归一化, 一般为不需要 GL_FALSE
参数 5: stride 第一次数据读取和第二次数据读取的间隔: 5 (第一个顶点数据的 x坐标值和第二个顶点数据坐标值 中间隔了 5 个值)
参数 6: ptr 读取数据的地址 (GLfloat 类型首地址开始读写, 偏移 0 个位置)
*/
glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 5, (GLfloat *)NULL + 0);
// 4.2 纹理坐标读取方式
/*
参数 1: indx, 读取对应数据类型的索引: GLKVertexAttribTexCoord0
参数 2: size, 读取该类型数据的步长: 2 (顶点数据坐标有三个 s, t)
参数 3: type, 该数据的数据类型: GL_FLOAT 类型
参数 4: normalized 是否需要归一化, 一般为不需要 GL_FALSE
参数 5: stride 第一次数据读取和第二次数据读取的间隔: 5 (第一个纹理顶点数据的 s坐标值和第二个纹理顶点数据坐标值 中间隔了 5 个值)
参数 6: ptr 读取数据的地址 (GLfloat 类型首地址开始读写, 偏移 3 个位置, 因为前面 3 个是顶点数据的 x,y,z, 从第 4 个开始才是纹理坐标 s,t 的值)
*/
glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 5, (GLfloat *)NULL + 3);
}
7.3 设置纹理代码
- (void)setupTexture
{
// 1. 获取图片路径
NSString *filePath = [[NSBundle mainBundle] pathForResource:@"test" ofType:@"jpg"];
// 2. 设置纹理相关参数
/*
openGL 默认的纹理坐标左下角为 (0,0), 但是 UIView 的左上角为(0,0), 设置了这个属性为 YES 后系统会自动帮我们翻转图片, 如果为 NO 的话我们的显示的效果是倒的
*/
NSDictionary *options = @{GLKTextureLoaderOriginBottomLeft: @(YES)};
NSError *error;
GLKTextureInfo *textureInfo = [GLKTextureLoader textureWithContentsOfFile:filePath options:options error:&error];
if (error) {
NSLog(@"GLKTextureLoader 生成 GLKTextureInfo 失败:%@", error);
return;
}
// 3. 用 GLKBaseEffect 完成着色器的工作
// GLKBaseEffect 是 Apple 封装的 OpenGL 的一个工具, 可以完成着色器相关的工作, 方便开发者使用
baseEffect = [[GLKBaseEffect alloc] init];
baseEffect.label = @"kun_texture";
baseEffect.texture2d0.name = textureInfo.name;
baseEffect.texture2d0.enabled = GL_TRUE;
}
7.4 GLKViewDelegate 代码
// 绘制视图内容
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
{
// 1. 清除 颜色/深度/模板缓冲区
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
// 2. baseEffect 准备好可以开始绘图
[baseEffect prepareToDraw];
// 3. 绘图
/*
glDrawArrays (GLenum mode, GLint first, GLsizei count)
参数 1: mode: 使用什么图元绘制
参数 2: first: 从顶点数组中第几个顶点开始绘制
参数 3: count: 总共多少个顶点 (一个正方形分两个三角形绘制,总共 6 个)
*/
glDrawArrays(GL_TRIANGLES, 0, 6);
}