OpenGL学习(九)-- OpenGL ES 初探(下)GLKit

个人 OpenGL 专题学习目录,但愿和你们一块儿学习交流进步!设计模式


1、GLKit 框架简介

GLKit 框架的设计⽬标是为了简化基于 OpenGL / OpenGL ES 的应用开发。它的出现加快 OpenGL ESOpenGL 应⽤程序开发。使⽤数学库,背景纹理加载,预先建立的着色器效果,以及标准视图和视图控制器来实现渲染循环。数组

  • 不用本身写着色器: GLKit 框架提供了功能和类,能够减小建立新的基于着色器的应⽤程序所需的⼯做量,或者支持依赖早期版本的 OpenGL ESOpenGL 提供的固定函数顶点或片断处理的现有应用程序。
  • 功能:
  • 一、加载纹理
  • 二、提供高性能的数学运算
  • 三、提供常⻅的着⾊器
  • 四、提供视图以及视图控制器

简单的来讲,GLKit 就是为了让 iOS 开发者在使用OpenGL ESOpenGL 的时候更简便更容易上手,封装了一堆库,咱们直接只写核心代码就好了。缓存

虽然苹果弃用 OpenGL ES ,但 iOS 开发者能够继续使用。服务器

2、用 GLKit 进行视图渲染

一、GLKView

GLKView 继承 UIView,提供绘制场所(View)。 下面看一下 GLKView 使用 OpenGL ES 绘制内容的视图默认实现:框架

  • 一、初始化视图 - (instancetype)initWithFrame:(CGRect)frame context:(EAGLContext *)context; 初始化新视图。ide

  • 二、设置视图的代理函数

  • 三、配置帧缓冲区对象 drawableColorFormat颜色缓冲区 的格式 drawableDepthFormat深度缓冲区 的格式 drawableStencilFormat模板缓冲区 的格式 drawableMultisample多重采样缓冲区 的格式post

  • 四、设置帧缓冲区属性 drawableHeight底层缓存区对象的高度(以像素为单位) drawableWidth底层缓存区对象的宽度(以像素为单位)性能

  • 五、绘制视图的内容 context 存储绘制视图内容时使用的 OpenGL ES 上下文状态。 - (void)bindDrawable; 将底层 FrameBuffer 对象绑定到 OpenGL ES enableSetNeedsDisplay 布尔值,指定视图是否响应使得视图内容无效的消息。 - (void)display; 当即重绘视图内容。 snapshot UIImage 类型,绘制视图内容并将其做为新图像对象返回。学习

  • 六、删除视图 FrameBuffer 对象 - (void)deleteDrawable; 删除与视图关联的可绘制对象。

  • 七、实现 GLKViewDelegate 代理方法 - (void)glkView:(GLKView *)view drawInRect:(CGRect)rect; 绘制视图内容(必须实现代理

二、GLKViewController

GLKViewController 继承 UIViewController,(扩展于标准的 UIKit 设计模式,用于绘制视图内容的管理与呈现)

  • 一、配置帧速率

    preferredFramesPerSecond 视图控制器调用视图以及更新视图内容的速率,默认为 30

    framesPerSecond 视图控制器调用视图以及更新视图内容的实际速率。

  • 二、配置 GLKViewController 代理

  • 三、控制帧更新: paused 布尔值,渲染循环是否已暂停。 pauseOnWillResignActive 布尔值,当前程序从新激活活动状态时视图控制器是否自动暂停渲染循环。 resumeOnDidBecomeActive 布尔值,当前程序变为活动状态时视图控制是否自动恢复呈现循环。

  • 四、获取有关 View 的更新信息: framesPerSecond 视图控制器自建立以来发送的帧更新数。 timeSinceFirstResume 视图控制器第一次恢复发送更新事件以来通过的时间量。 timeSinceLastResume 自上次视图控制器恢复发送更新事件以来更新的时间量。 timeSinceLastUpdate 自上次视图控制器调用委托方法以及通过的时间量。 timeSinceLastDraw 自上次视图控制器调用视图 display 方法以来通过的时间量。

  • 五、实现代理方法:

    - (void)glkViewControllerUpdate:(GLKViewController *)controller;处理更新事件

    - (void)glkViewController:(GLKViewController *)controller willPause:(BOOL)pause; 暂停/恢复通知

三、GLKBaseEffect

GLKBaseEffectGLKit 提供的一种简单的光照/着色系统,用于基于着色器 OpenGL 渲染。

  • 一、命名 Effect:

    labelEffect(效果) 命名。

  • 二、配置模型视图转换:

    transform 绑定效果时应用于顶点数据的模型视图,投影和纹理变换。

  • 三、配置光照效果:

    lightingType 用于计算每一个片断的光照策略,GLKLightingType

typedef NS_ENUM(GLint, GLKLightingType)
{
    GLKLightingTypePerVertex,
    GLKLightingTypePerPixel
} NS_ENUM_AVAILABLE(10_8, 5_0);
复制代码

GLKLightingTypePerVertex 表示在三⻆形中每一个顶点执行光照计算,而后在三⻆形进⾏插值。 GLKLightingTypePerPixel 表示光照计算的输入在三角形内插入,而且在每一个⽚段执行光照计算。

  • 四、配置光照:

    lightModelTwoSided 布尔值,表示为基元的两侧计算光照。 material 计算渲染图元光照使⽤的材质属性。 lightModelAmbientColor 环境颜⾊,应⽤效果渲染的全部图元。 light0light1light2 分别为场景中第 一、二、3 个光照属性。 注意: GLKit 最多就支持3个光照。

  • 五、配置纹理:

    texture2d0readonly 的,第一个纹理属性。 texture2d1readonly 的,第二个纹理属性。 textureOrder 纹理应⽤于渲染图元的顺序。 **注意:**最多就支持俩纹理,三个光照,因此 GLKit 有局限性,若是要支持多个纹理,就不能用 GLKit 了,得本身写了。

  • 六、配置雾化:

    fog 应用于场景的雾属性。

  • 七、配置颜色信息:

    colorMaterialEnabled 布尔值,表示计算光照与材质交互时是否使用颜色顶点属性。 useConstantColor 布尔值,指示是否使用常量颜⾊。 constantColor 不提供每一个顶点颜色数据时使⽤的常量颜⾊。

  • 八、准备绘制效果:

    - (void) prepareToDraw; 准备渲染效果(绘制时同步全部效果更改以保持一致状态)。注意:绘制以前必须写

3、GLKit 牛刀小试

思惟导图以下:

OpenGL ES GLKit 图片加载   by 凡几多.png

首先我建立一个带默认 storyboard 的工程,为了方便,直接把自带的 ViewClass类型改成了 GLKView,固然咱们也能够用代码 alloc 建立。

GLKView.png

而后咱们在 .h 文件中导入头文件 GLKit,而且把 ViewController 的父类改成 GLKViewController

#import <UIKit/UIKit.h>
#import <GLKit/GLKit.h>

@interface ViewController : GLKViewController
复制代码

接下来在 .m 文件中导入头文件。

#import <OpenGLES/ES3/gl.h>
#import <OpenGLES/ES3/glext.h>
复制代码

定义两个全局变量 EAGLContextGLKBaseEffect

@interface ViewController ()
{
    EAGLContext *context;
    GLKBaseEffect *cEffect;
}
@end
复制代码

一、OpenGL ES 相关初始化

先来建立一个方法,命名为 setUpConfig,用来进行 OpenGL ES 的相关初始化

1)、初始化上下文 & 设置当前上下文

  • 初始化上下文:
context = [[EAGLContext alloc]initWithAPI:kEAGLRenderingAPIOpenGLES3];
复制代码

EAGLContext 是苹果 iOS 平台下实现 OpenGL ES 渲染层。 参数表明使用哪一种 OpenGL ESAPI 初始化,OpenGL ES 1 使用的是固定管线23 差异不大。

kEAGLRenderingAPIOpenGLES1 = 1, kEAGLRenderingAPIOpenGLES2 = 2, kEAGLRenderingAPIOpenGLES3 = 3

  • 设置当前上下文:
[EAGLContext setCurrentContext:context];
复制代码

2)、获取GLKView & 设置context

GLKView *view = (GLKView *) self.view;
view.context = context;
复制代码

3)、配置视图建立的渲染缓存区

(1). drawableColorFormat:颜色缓存区格式

简介:OpenGL ES 有一个缓存区,它用以存储将在屏幕中显示的颜色。你可使用其属性来设置缓冲区中的每一个像素的颜色格式。

view.drawableColorFormat = >GLKViewDrawableColorFormatRGBA8888;
复制代码

GLKViewDrawableColorFormatRGBA8888 = 0 默认缓存区的每一个像素的最小组成部分 (RGBA) 使用 8bit,(因此每一个像素 4 个字节,4 * 8bit)。 GLKViewDrawableColorFormatRGB565, 若是你的 APP 容许更小范围的颜色,便可设置这个。会让你的 APP 消耗更小的资源(内存和处理时间)

(2). drawableDepthFormat:深度缓存区格式

view.drawableDepthFormat = GLKViewDrawableDepthFormat16;
复制代码

GLKViewDrawableDepthFormatNone = 0 意味着彻底没有深度缓冲区 GLKViewDrawableDepthFormat16 GLKViewDrawableDepthFormat24 若是你要使用这个属性(通常用于 3D 游戏),你应该选择 GLKViewDrawableDepthFormat16GLKViewDrawableDepthFormat24。这里的差异是使用 GLKViewDrawableDepthFormat16 将消耗更少的资源。

下面为 setUpConfig 方法完整代码:

- (void)setUpConfig {
    // 1.初始化上下文&设置当前上下文
    context = [[EAGLContext alloc]initWithAPI:kEAGLRenderingAPIOpenGLES3];
    //判断context是否建立成功
    if (!context) {
        NSLog(@"Create ES context Failed");
    }
    //设置当前上下文
    [EAGLContext setCurrentContext:context];
    
    //2.获取GLKView & 设置context
    GLKView *view =(GLKView *) self.view;
    view.context = context;
        
    //3.配置视图建立的渲染缓存区.
    view.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888;
    view.drawableDepthFormat = GLKViewDrawableDepthFormat16;
    
    //4.设置背景颜色
    glClearColor(1, 0, 0, 1.0);
}
复制代码

二、加载顶点/纹理坐标数据

再建立一个方法,命名为 setUpVertexData,用来加载顶点/纹理坐标数据。

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, //左下
   };
复制代码

这里一共是两个三角形组成的,因此是六个顶点。前三个元素组成顶点坐标,第四个和第五个元素组成二维的纹理坐标,后面以此类推。 纹理坐标系取值范围 [0,1];原点是左下角 (0,0);故而 (0,0) 是纹理图像的左下角, 点 (1,1) 是右上角.

2)开辟顶点缓存区:

  • 顶点数组: 开发者能够选择设定函数指针,在调用绘制方法的时候,直接由内存传入顶点数据,也就是说这部分数据以前是存储在内存当中的,被称为顶点数组
  • 顶点缓存区: 性能更高的作法是,提早分配一块显存,将顶点数据预先传入到显存当中。这部分的显存,就被称为顶点缓冲区。

(1)建立顶点缓冲区标识符 ID

GLuint bufferID;
glGenBuffers(1, &bufferID);
复制代码

glGenBuffers(GLsizei n, GLuint *buffers)的第一个参数是代表有 1 个缓冲区。顶点缓冲对象(Vertex Buffer Objects, VBO)

(2)绑定顶点缓存区(明确做用)

glBindBuffer(GL_ARRAY_BUFFER, bufferID);
复制代码

glBindBuffer(GLenum target, GLuint buffer) 的第一个参数表明是作什么用的,GL_ARRAY_BUFFER 表明数组缓冲区。

(3)将顶点数组的数据 copy 到顶点缓存区中(内存——>GPU 显存中)

glBufferData(GL_ARRAY_BUFFER, sizeof(vertexData), vertexData, GL_STATIC_DRAW);
复制代码

glBufferData(GLenum target, GLsizeiptr size, const GLvoid *data, GLenum usage)

target 参数: 指定是什么类型的数据,和上面 glBindBuffer 中的保持一致。

size 参数: 这个数据有多大。

data 参数: 数据的地址。这里由于是数组,因此数组名就是它的首地址。

usage 参数: 绘制方式,静态绘制仍是动态绘制。

3)打开读取通道:

(1)默认是关闭的

iOS 中, 默认状况下,出于性能考虑,全部顶点着色器的属性 (Attribute) 变量都是关闭的。 意味着,顶点数据在着色器端(服务端)是不可用的。即便你已经使用 glBufferData 方法,将顶点数据从内存拷贝到顶点缓存区中(GPU 显存中)。 因此,必须由 glEnableVertexAttribArray 方法打开通道,指定访问属性,才能让顶点着色器可以访问到从 CPU 复制到 GPU 的数据。

注意: 数据在 GPU 端是否可见,即着色器可否读取到数据,由是否启用了对应的属性决定,这就是 glEnableVertexAttribArray 的功能,容许顶点着色器读取 GPU(服务器端)数据。

(2)方法简介 A、glEnableVertexAttribArray 方法:

glEnableVertexAttribArray(GLuint index)
复制代码

功能: 打开对应 attribute 通道的开关。

index 参数: 表明属性通道 顶点 GLKVertexAttribPosition, 法线 GLKVertexAttribNormal, 颜色值 GLKVertexAttribColor, 纹理1 GLKVertexAttribTexCoord, 纹理2 GLKVertexAttribTexCoord1

B、glVertexAttribPointer 方法:

glVertexAttribPointer (GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid* ptr)
复制代码

功能: 上传顶点数据到显存的方法(设置合适的方式从buffer里面读取数据)

  • 参数列表:

一、indx 参数: 指定要修改的顶点属性的索引值

二、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,通常设为 GL_FALSE

五、stride 参数: 指定连续顶点属性之间的偏移量。若是为 0,那么顶点属性会被理解为:它们是紧密排列在一块儿的。初始值为 0

六、ptr 参数: 指定一个指针,指向数组中第一个顶点属性的第一个组件。初始值为 0

下面为 setUpVertexData 方法完整代码:

- (void)setUpVertexData {
    // 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, //左下
    };
    //2.开辟顶点缓存区
    //(1).建立顶点缓存区标识符ID
    GLuint bufferID;
    glGenBuffers(1, &bufferID);
    //(2).绑定顶点缓存区.(明确做用)
    glBindBuffer(GL_ARRAY_BUFFER, bufferID);
    //(3).将顶点数组的数据copy到顶点缓存区中(GPU显存中)
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertexData), vertexData, GL_STATIC_DRAW);
    
    //3.打开读取通道.
    //顶点坐标数据
    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);
}
复制代码

三、加载纹理数据(使用GLBaseEffect)

再建立一个方法,命名为 setUpTexture,用来加载纹理数据(使用 GLBaseEffect)。

注意: 由于纹理原点是:左下角(0,0)view 原点是:左上角(0,0); 因此在设置纹理的 options 参数时,须要传 GLKTextureLoaderOriginBottomLeft 翻转一下,否则纹理就是倒着的。这是 GLKit 里的解决办法,在 OpenGL ES 里就没这么方便了。

- (void)setUpTexture {
    //1.获取纹理图片路径
    NSString *filePath = [[NSBundle mainBundle]pathForResource:@"凡几多" ofType:@"jpg"];
    
    //2.设置纹理参数
    //纹理坐标原点是左下角,可是图片显示原点应该是左上角.
    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;
}
复制代码

四、实现代理方法 GLKViewDelegate

GLKView 对象使其 OpenGL ES 上下文成为当前上下文,并将其 framebuffer 绑定为 OpenGL ES 呈现命令的目标。而后,委托方法应该绘制视图的内容。

//绘制视图的内容
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect {
    //1.
    glClear(GL_COLOR_BUFFER_BIT);
    
    //2.准备绘制
    [cEffect prepareToDraw];
    
    //3.开始绘制,用三角形,从第0个顶点开始画,一共画6个
    glDrawArrays(GL_TRIANGLES, 0, 6);
}
复制代码

五、最终调用

- (void)viewDidLoad {
    [super viewDidLoad];
    //1.OpenGL ES 相关初始化
    [self setUpConfig];
    
    //2.加载顶点/纹理坐标数据
    [self setUpVertexData];
    
    //3.加载纹理数据(使用GLBaseEffect)
    [self setUpTexture];  
}
复制代码

转载请备注原文出处,不得用于商业传播——凡几多

相关文章
相关标签/搜索