美文网首页
iOS开发-使用OpenGL ES 加载图片

iOS开发-使用OpenGL ES 加载图片

作者: 泽泽伐木类 | 来源:发表于2020-07-25 14:12 被阅读0次

    前言

    在iOS中我们通常使用UIKit下的UIImageView来创建并显示一张图片,如下:

    - (void)iosDefaultFunc
    {
        UIImageView *imageView = [[UIImageView alloc]initWithFrame:self.view.bounds];
        imageView.image = [UIImage imageNamed:@"xxx.png"];
        [self.view addSubview:imageView];
    }
    

    而这个简单调用的背后,却是经历了一段复杂的过程,才显示到我们的屏幕。
    大致流程:图片解压缩->获取位图信息->顶点数据->顶点缓冲区->顶点着色->图元装配->片元着色->帧缓冲区->屏幕。在最新的iOS系统中,这个内部的处理是通过Metal来完成的。今天我们就尝试使用GLKit来实现一个图片的渲染。

    开始

    初始化OpenGL ES

    • EAGLContext: 状态上下文(状态机),保存OpenGL状态信息
    • GLKView: UIView子类。OpenGL本身不提供窗口组件,在iOS的GLKit中,使用GLKView来呈现最终渲染的画面。
    • GLKViewController: UIViewController子类,遵守并实现了GLKViewDelegate协议。
    #pragma mark - 初始化OpenGL ES
    - (void)configAndLoadOpenGL_ES
    {
        //1.初始化上下文
        context = [[EAGLContext alloc]initWithAPI:kEAGLRenderingAPIOpenGLES3];
        if(!context){
            NSLog(@"上下文创建失败");
            return;
        }
        //设置上下文
        [EAGLContext setCurrentContext:context];
        //2.创建并添加gl渲染窗口View
        GLKView *glView = (GLKView *)self.view;
        glView.context = context;
        //配置渲染缓冲区
        //GLKViewDrawableColorFormatRGBA8888 (r(8bit),g(8bit),b(8bit),a(8bit)) (An RGBA8888 format.)
        glView.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888;
        //深度测试精度为24(A 24-bit depth entry for each pixel.)
        glView.drawableDepthFormat = GLKViewDrawableDepthFormat16;
        //3.设置背景颜色176,196,222
        glClearColor(1, 0, 0, 1.0);
    }
    
    • 实现GLKViewDelegate协议方法
    - (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
    {
        glClear(GL_COLOR_BUFFER_BIT);
    }
    

    创建并初始化顶点和纹理数据

    - (void)loadVertexData
    {
        //1.设置定点和纹理数据
        // x,y,z,s,t
        GLfloat vertexData[] = {
            0.25, -0.5, 0.0f,    1.0f, 0.0f, //右下
            0.25, 0.5,  0.0f,    1.0f, 1.0f, //右上
            -0.25, 0.5, 0.0f,    0.0f, 1.0f, //左上
            0.25, -0.5, 0.0f,    1.0f, 0.0f, //右下
            -0.25, 0.5, 0.0f,    0.0f, 1.0f, //左上
            -0.25, -0.5, 0.0f,   0.0f, 0.0f, //左下
        };
        //2.开辟定点缓冲区
        //创建顶点缓冲区标识ID
        GLuint bufferID;
        glGenBuffers(1, &bufferID);
        //绑定顶点缓冲区
        glBindBuffer(GL_ARRAY_BUFFER, bufferID);
        //将顶点数据copy到顶点缓冲区(CPU->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(GLKVertexAttribPosition, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 5, (GLfloat *)NULL + 3);
    }
    
    • 关于打开读取通道:
      在iOS中, 默认情况下,出于性能考虑,所有顶点着色器的属性(Attribute)变量都是关闭的。这就意味着,顶点数据在着色器端(服务端)是不可用的. 即使你已经使用glBufferData方法,将顶点数据从内存拷贝到顶点缓存区中(GPU显存中)。所以, 必须由glEnableVertexAttribArray方法打开通道.指定访问属性.才能让顶点着色器能够访问到从CPU复制到GPU的数据。
      注意: 数据在GPU端是否可见,即,着色器能否读取到数据,由是否启用了对应的属性决定,这就是glEnableVertexAttribArray的功能,允许顶点着色器读取GPU(服务器端)数据。
    • glVertexAttribPointer()参数解释:
    /// 设置顶点数据读取方式
    /// @param indx 顶点属性的索引值
    /// @param size 每次读取的数量
    /// @param type 数据类型
    /// @param normalized 指定当被访问时,固定点数据值是否应该被归一化(GL_TRUE)或者直接转换为固定点值(GL_FALSE)
    /// @param stride 数据读取偏移量
    /// @param ptr 数据读取的起始位置
    glVertexAttribPointer (GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid* ptr)
    

    载入纹理

    • GLKTextureLoader:在GLKit中使用使用该类载入一个纹理数据,并返回一个GLKTextureInfo类型的对象,该对象描述了纹理相关信息
    • GLKBaseEffect:一个简单的着色器类,内部封装了顶点/片元着色器。(该类最多同时包含两个纹理数据)
    - (void)loadTextureData
    {
        NSString *path = [[NSBundle mainBundle]pathForResource:@"jj" ofType:@"jpg"];
        NSDictionary *options = @{
            //转换图像数据以匹配OpenGL的左下方向规范
            GLKTextureLoaderOriginBottomLeft:@(YES)
        };
        NSError *error = nil;
        //1.获取纹理信息
        GLKTextureInfo *textureInfo = [GLKTextureLoader textureWithContentsOfFile:path options:options error:&error];
        if(error){
            NSLog(@"error == %@",error);
        }
        //2.GLKit提供GLKBaseEffect 完成着色器工作(顶点/片元)。最多可加载2个纹理
        effect = [[GLKBaseEffect alloc]init];
        effect.texture2d0.enabled = GL_TRUE;
        effect.texture2d0.name = textureInfo.name;
    }
    

    最后在GLKViewDelegate的代理方法中,触发绘制:

    #pragma mark -- GLKViewDelegate
    - (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
    {
        printf("%s",__func__);
        //清空颜色缓冲区
        glClear(GL_COLOR_BUFFER_BIT);
        //准备绘制
        [effect prepareToDraw];
        //设置图元装配方式,起点,长度
        glDrawArrays(GL_TRIANGLES, 0, 6);
    }
    

    效果

    最终效果图

    相关文章

      网友评论

          本文标题:iOS开发-使用OpenGL ES 加载图片

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