美文网首页OpenGL
九、OpenGL ES 之 GLKit常用API & 加载图片

九、OpenGL ES 之 GLKit常用API & 加载图片

作者: 夏天的枫_ | 来源:发表于2020-07-28 11:44 被阅读0次

Apple OpenGL ES Documentation

  • GLKit的功能
    1.加载纹理
    2.提供高性能的数学运算
    3.提供常见的着色器
    4.提供视图以及视图控制器

创建和配置GLKit 视图
GLKit视图会自动创建并配置自己的OpenGL ES帧缓冲区对象和渲染缓冲区。您可以使用视图的drawable属性控制这些对象的属性,如下所示。如果更改GLKit视图的大小,比例因子或可绘制属性,则它会在下次绘制其内容时自动删除并重新创建适当的帧缓冲区对象和渲染缓冲区。

-(void)viewDidLoad
{
    [super viewDidLoad];
 
    //创建一个OpenGL ES上下文,并将其分配给从情节提要加载的视图
    GLKView * view =(GLKView *)self.view;
    view.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
 
    //配置视图创建的渲染缓冲区
    view.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888;
    view.drawableDepthFormat = GLKViewDrawableDepthFormat24;
    view.drawableStencilFormat = GLKViewDrawableStencilFormat8;
 
    //启用多重采样
    view.drawableMultisample = GLKViewDrawableMultisample4X;
}

绘制OpenGL ES内容的三个步骤:准备OpenGL ES基础结构,发出绘制命令以及将渲染的内容呈现给Core Animation显示。本GLKView类实现了第一和第三个步骤。第二步,实现如下示例所示的绘制方法。

-(void)drawRect:(CGRect)rect
{
    //清除帧缓冲区
    glClearColor(0.0f,0.0f,0.1f,1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 
    //使用先前配置的纹理,着色器,制服和顶点数组进行绘制
    glBindTexture(GL_TEXTURE_2D,_planetTexture);
    glUseProgram(_diffuseShading);
    glUniformMatrix4fv(_uniformModelViewProjectionMatrix,1,0,_modelViewProjectionMatrix.m);
    glBindVertexArrayOES(_planetMesh);
    glDrawElements(GL_TRIANGLE_STRIP,256,GL_UNSIGNED_SHORT);
}

注意: 该glClear函数向OpenGL ES暗示可以丢弃任何现有的帧缓冲区内容,从而避免了将先前内容加载到内存中的昂贵内存操作。为确保最佳性能,应始终在绘制之前调用此函数。
GLKView班是能够提供对OpenGL ES绘制,因为它管理的OpenGL ES渲染过程的标准件简单的界面:

  • 在调用绘图方法之前,视图:

    • 使它的EAGLContext对象成为当前上下文

    • 根据其当前大小,比例因子和可绘制属性(如果需要)创建一个帧缓冲区对象和渲染缓冲区

    • 将帧缓冲区对象绑定为绘图命令的当前目标

    • 设置OpenGL ES视口以匹配帧缓冲区的大小

  • 返回绘制方法后,视图:

    • 解析多重采样缓冲区(如果启用了多重采样)

    • 丢弃不再需要其内容的渲染缓冲区

    • 将渲染缓冲区内容呈现给Core Animation进行缓存和显示

GLKit 纹理理加载

  • name : OpenGL 上下⽂文中纹理理名称
  • target : 纹理理绑定的⽬目标
  • height : 加载的纹理理⾼高度
  • width : 加载纹理理的宽度
  • textureOrigin : 加载纹理理中的原点位置
  • alphaState: 加载纹理理中alpha分量量状态•containsMipmaps: 布尔值,加载的纹理理是否包含mip贴图
  • 初始化
    - initWithSharegroup: 初始化一个新的纹理加载到对象中
    - initWithShareContext: 初始化一个新的纹理加载对象
  • 从文件中加载纹理
    + textureWithContentsOfFile:options:errer: 从⽂文件加载2D纹理理图像并从数据中创建新的纹理理
    -textureWithContentsOfFile:options:queue:completionHandler: 从⽂文件中异步加载2D纹理理图像,并根据数据创建新纹理

GLTextureLoader 简化从各种资源文件中加载纹理

  • 从URL加载纹理理
    -textureWithContentsOfURL:options:error: 从URL 加载2D纹理理图像并从数据创建新纹理
    -textureWithContentsOfURL:options:queue:completionHandler: 从URL异步加载2D纹理图像,并根据数据创建新纹理理.

  • 从内存中表示创建纹理
    +textureWithContentsOfData:options:errer: 从内存空间加载2D纹理理图像,并根据数据创建新纹理
    -textureWithContentsOfData:options:queue:completionHandler:从内存空间异步加载2D纹理理图像,并从数据中创建新纹理

  • 从CGImages创建纹理理
    - textureWithCGImage:options:error: 从Quartz图像加载2D纹理理图像并从数据创建新纹理
    - textureWithCGImage:options:queue:completionHandler: 从Quartz图像异步加载2D纹理理图像,并根据数据创建新纹理.

  • 从URL加载多维创建纹理
    + cabeMapWithContentsOfURL:options:errer: 从单个URL加载⽴方体贴图纹理图像,并根据数据创建新纹理理
    -cabeMapWithContentsOfURL:options:queue:completionHandler:从单个URL异步加载⽴方体贴图纹理图像,并根据数据创建新纹理

  • 从⽂文件加载多维数据创建纹理理
    + cubeMapWithContentsOfFile:options:errer: 从单个⽂文件加载⽴⽅体贴图纹理理对象,并从数据中创建新纹理
    -cubeMapWithContentsOfFile:options:queue:completionHandler:从单个文件异步加载立方体贴图纹理理对象,并从数据中创建新纹理
    + cubeMapWithContentsOfFiles:options:errer: 从⼀一系列⽂件中加载⽴⽅体贴图纹理图像,并从数据总创建新纹理
    -cubeMapWithContentsOfFiles:options:options:queue:completionHandler: 从一系列文件异步加载⽴方体贴图纹理图像,并从数据中创建新纹理

GLKView使用OpenGL ES 绘制内容的视图默认实现

  • 初始化视图
    - initWithFrame:context: 初始化新视图
  • 代理
    -delegate 视图的代理
  • 配置帧缓存区对象
    drawableColorFormat 颜⾊色渲染缓存区格式
    drawableDepthFormat 深度渲染缓存区格式
    drawableStencilFormat 模板渲染缓存区的格式
    drawableMultisample 多重采样缓存区的格式
  • 帧缓存区属性
    drawableHeight 底层缓存区对象的高度(以像素为单位)
    drawableWidth 底层缓存区对象的宽度(以像素为单位)

GLKViewDelegate 用于GLKView对象回调方法
-glkView:drawInRect: 绘制视图内容(必须实现的代理)

加载纹理图片

//
//  ViewController.m
//  OpenGLES
//
//  Created by Brain on 2020/7/26.
//  Copyright © 2020年 Brain. All rights reserved.
//

#import "ViewController.h"
#import <OpenGLES/ES3/gl.h>




@interface ViewController ()

@property (strong,nonatomic) EAGLContext * context;

@property (nonatomic,strong) GLKBaseEffect * myEffect;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    // 初始化 创建context
    [self initContext];
    
    // 使用 GLBaseEffect 加载纹理数据
    [self loadTexture];
    
    // 加载订单+纹理坐标数据
    [self initVertexDataAndTextureCoord];
    
   
    
}
- (void)initContext
{
    // 1.初始化上下文,kEAGLRenderingAPIOpenGLES3:使用3.0的OpenGL ES
    self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];
    if (!self.context) {
        NSLog(@"context init failed");
    }
    // 2.设置当前上下文
    [EAGLContext setCurrentContext:self.context];
    // 3.更改了self.view,让其继承自GLKView,赋值实例化
    GLKView * view = (GLKView *)self.view;
    view.context = self.context;
    
    
    /**
     4. 设置视图创建的渲染缓存区
     1). drwableColorFormat: 颜色缓存格式
     2). GLKViewDrawbleColorFormatRGBA8888 缓存区的每个像素的最小组成部分(RGBA)使用8个bit,(所以每个像素4个字节,4*8个bit)。
     */
    view.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888;
    
    /**
     5. 设置视图创建的渲染缓存区
     1). drawableDepthFormat: 深度缓存格式
     2). GLKViewDrawableDepthFormatNone = 0,意味着完全没有深度缓冲区
         GLKViewDrawableDepthFormat16,
         GLKViewDrawableDepthFormat24,
         ...16,...24 一般用于3D场景or游戏,差别是使用...Format16消耗更少的资源
     */
    view.drawableDepthFormat = GLKViewDrawableDepthFormat16;
    
    
    glClearColor(1, 0.7, 0.3, 1);
}

/**
 加载顶点数据 & 纹理坐标
 */
- (void)initVertexDataAndTextureCoord
{
    // 1.假设本地有一图片,获取其路径
    NSString * imgPath = [[NSBundle mainBundle]pathForResource:@"timg" ofType:@"jpg"];

    
    // 2.设置纹理参数  显示图片时,原点是在左上角,而对于纹理坐标,原点是从左下角开始的bottomLeft,GLKTextureLoaderOriginBottomLeft 默认为false
    NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:@(1),GLKTextureLoaderOriginBottomLeft, nil];
    
    GLKTextureInfo *textureInfo = [GLKTextureLoader textureWithContentsOfFile:imgPath options:options error:nil];
    
    // 3.苹果使用GLKit中的GLKitBaseEffect 完成顶点+片元着色器操作
    _myEffect = [[GLKBaseEffect alloc]init];
    _myEffect.texture2d0.enabled = GL_TRUE;
    _myEffect.texture2d0.name = textureInfo.name;
    
}

/**
 加载纹理数据
 */
- (void)loadTexture
{
    /**
     1.先设置顶点数据,它包括了顶点坐标+纹理坐标
     纹理坐标取值范围为[0,1],左下角原点为 (0,0);所以设置(0,0)为纹理图片的左下角,则右上角就是(1,1);
       每行前3位顶点坐标,后2为纹理坐标,可以用两个数组表示,OpenGL 普遍使用一个一位数组设置;
       还可以使用结构体转载 顶点坐标 + 纹理坐标 + 法向量。
     */
    
    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, //左下
    };
    
    /*
     顶点数组: 开发者可以选择设定函数指针,在调用绘制方法的时候,直接由内存传入顶点数据,也就是说这部分数据之前是存储在内存当中的,被称为顶点数组
     顶点缓存区: 性能更高的做法是,提前分配一块显存,将顶点数据预先传入到显存当中。这部分的显存,就被称为顶点缓冲区
     copy vertexData 内存 --> 显存,提示性能
     */
    
    // 2. 开辟顶点缓存区
    // 1).创建顶点缓存区标识符bufferID
    GLuint bufferID;
    glGenBuffers(1, &bufferID); // 这里开辟一个缓存区即可,传入数据的指针地址
    
    // 2).绑定顶点缓存区,为数组类型
    glBindBuffer(GL_ARRAY_BUFFER, bufferID);
    
    // 3).将顶点数组中的数据拷贝到开辟的顶点缓存区
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertexData), vertexData, GL_STATIC_DRAW);
    
    // 3.打开读取通道,加载顶点坐标数据 和 纹理坐标数据
    // 顶点坐标数据
    /**
     在iOS中, 默认情况下,出于性能考虑,所有顶点着色器的属性(Attribute)变量都是关闭的.
     意味着,顶点数据在着色器端(服务端)是不可用的. 即使你已经使用glBufferData方法,将顶点数据从内存拷贝到顶点缓存区中(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
     */
    
    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);
    
    
}

#pragma mark - GLKView delegate
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
{
    // 清理缓存区
    glClear(GL_COLOR_BUFFER_BIT);
    
    // 准备绘制
    [_myEffect prepareToDraw];
    
    // 开始绘制
    // 采用三角形批次绘制,从0开始,用到了上下三角,6个点
    glDrawArrays(GL_TRIANGLES, 0, 6);
    
    
    
}

@end

利用GLKit加载纹理图片Demo

相关文章

网友评论

    本文标题:九、OpenGL ES 之 GLKit常用API & 加载图片

    本文链接:https://www.haomeiwen.com/subject/jdoxrktx.html