美文网首页iOS的新技术
OpenGLES学习之路-矩形

OpenGLES学习之路-矩形

作者: Justin910 | 来源:发表于2017-02-27 11:56 被阅读251次

    前言

    • 因为在书上和网上看到说写Blog各种好, 所以就来尝试一下,虽然不会有人看,但是没关系毕竟可以巩固和更加深入的了解.
    • 我公司所处的行业是安防监控, 所以刚好有机会接触音视频相关的内容.虽然我是个菜鸟, 但是菜鸟也会有春天的O(∩_∩)O哈哈~.
    • 在这里跟大家一起学习OpenGLES,一起进步.
    • 本系列不会讲太多的理论知识, 会讲解尽量多的用法, 毕竟了解更多的用法在实际使用中可以产生更多想法.

    最终效果

    实现流程

    • 1.设置上下文
    • 2.设置Layer
    • 3.设置渲染缓冲区
    • 4.设置帧缓冲区
    • 5.创建顶点着色器和片段着色器
    • 6.编译着色器
    • 7.链接着色器
    • 8.声明矩形四个角对应的数据
    • 9.开始渲染
    • 10.释放资源

    自定义的OpenGLView有点小复杂, 实现过程比系统提供的GLKView复杂了一丢丢.但是可以更深入的了解OpenGLES实现过程

    实现过程

    1.设置上下文

    /**
     *  设置上下文
     */
    - (void)setupContext {
        
        _context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];
        if (!_context) {
            
            _context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
        }
        
        if (![EAGLContext setCurrentContext:_context]) {
            NSLog(@"Failed to set current OpenGL context");
            exit(1);
        }
    }
    

    2.设置Layer

    +(Class)layerClass {
        return [CAEAGLLayer class];
    }
    
    - (void)setupLayer {
        _eaglLayer = (CAEAGLLayer*)self.layer;
        _eaglLayer.opaque = YES;
    }
    

    3.设置渲染缓冲区

    /**
     *  创建一个渲染缓冲
     */
    - (void)setupRenderBuffer {
        
        glGenRenderbuffers(1, &_renderBuffer);
        glBindRenderbuffer(GL_RENDERBUFFER, _renderBuffer);
        [_context renderbufferStorage:GL_RENDERBUFFER fromDrawable:_eaglLayer];
    }
    

    4.设置帧缓冲区

    /**
     *  设置帧缓冲区
     */
    - (void)setupFrameBuffer {
        
        glGenFramebuffers(1, &_frameBuffer);
        glBindFramebuffer(GL_FRAMEBUFFER, _frameBuffer);
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, _renderBuffer);
    }
    

    5.创建顶点着色器和片段着色器

    (1)创建顶点着色器RectangleVertex.glsl

    attribute vec4 Position;
    
    attribute vec4 InColor;
    varying vec4 OutColor;
    
    void main(void){
        OutColor = InColor;
        gl_Position = Position;
    }
    
    /*
        vec2.vec3.vec4.表示对应的234阶矩阵
        
        attribute修饰符表示从"应用程序"传过来的数据(也就是下面这些数据传过来)
         //4个顶点(分别表示xyz轴)
         static const float Vertices[] = {
         
             -0.5, -0.5, 0,  //左下
              0.5, -0.5, 0,  //右下
             -0.5,  0.5, 0,  //左上
              0.5,  0.5, 0,  //右上
         };
         
         //4个点的颜色(分别表示RGBA值)
         static const float Colors[] = {
             
             1,0,0,1,
             0,1,0,1,
             0,0,1,1,
             0,0,0,1,
         };
     
        varying修饰符是用于和片段着色器通讯的接口
     */
    

    (2)创建片段着色器RectangleFragment.glsl

    varying lowp vec4 OutColor;
    
    void main(void){
        gl_FragColor = OutColor;
    }
    
    

    上面的这些修饰符可以在这里看.

    6.编译着色器

    - (GLuint)compileShader:(NSString*)shaderName withType:(GLenum)shaderType {
        
        // 获取资源路径
        NSString *shaderPath = [[NSBundle mainBundle] pathForResource:shaderName ofType:@"glsl"];
        NSError  *error;
        NSString *shaderString = [NSString stringWithContentsOfFile:shaderPath
                                                           encoding:NSUTF8StringEncoding
                                                              error:&error];
        
        if (!shaderString) {
            
            NSLog(@"Error loading shader: %@", error.localizedDescription);
            exit(1);
        }
        
        /*
         *  GL_FRAGMENT_SHADER  创建一个片段着色器
         *  GL_VERTEX_SHADER    创建一个顶点着色器
         */
        GLuint shaderHandle          = glCreateShader(shaderType);
        
        // 转换成char*类型(给予OpenGL着色器)
        const char *shaderStringUTF8 = [shaderString UTF8String];
        int shaderStringLength       = (int)[shaderString length];
        
        /**
         *  @param shader      所产生的着色器名称
         *  @param count       表示多少资源传递一次(如果只上传一个着色代码,这里必须填1)
         *  @param string      C语言的资源路径
         *  @param length      C语言资源路径的字符长度
         */
        glShaderSource(shaderHandle, 1, &shaderStringUTF8, &shaderStringLength);
        
        // 调用运行时编译的着色器
        glCompileShader(shaderHandle);
        
        // 查看是否有错误,有的话获取错误信息
        GLint compileSuccess;
        glGetShaderiv(shaderHandle, GL_COMPILE_STATUS, &compileSuccess);
        if (compileSuccess == GL_FALSE) {
            
            GLchar messages[256];
            glGetShaderInfoLog(shaderHandle, sizeof(messages), 0, &messages[0]);
            NSString *messageString = [NSString stringWithUTF8String:messages];
            NSLog(@"%@", messageString);
            glDeleteShader(shaderHandle);
            exit(1);
        }
        
        return shaderHandle;
    }
    

    7.链接着色器

    /**
     *  编译着色器
     */
    - (void)compileShaders {
        
        //编译顶点着色器和片段着色器
        GLuint vertexShader   = [self compileShader:@"RectangleVertex" withType:GL_VERTEX_SHADER];
        GLuint fragmentShader = [self compileShader:@"RectangleFragment" withType:GL_FRAGMENT_SHADER];
        
        //把顶点和片段着色器链接到一个完整的程序
        _program = glCreateProgram();
        glAttachShader(_program, vertexShader);
        glAttachShader(_program, fragmentShader);
        
        //连接程序
        glLinkProgram(_program);
        
        //检查是否有错误, 有的话获取错误信息
        if(![self validateProgram:_program]) {
            
            glDeleteProgram(_program);
            exit(1);
        }
        
        //告诉OpenGL使用该程序
        glUseProgram(_program);
        
        //这里是获取刚才着色器里面的变量并使用
        _positionSlot = glGetAttribLocation(_program, "Position");
        _colorSlot    = glGetAttribLocation(_program, "InColor");
        glEnableVertexAttribArray(_positionSlot);
        glEnableVertexAttribArray(_colorSlot);
    }
    
    //验证链接程序是否有效
    - (BOOL)validateProgram:(GLuint)prog {
        
        GLint linkSuccess;
        glGetProgramiv(prog, GL_LINK_STATUS, &linkSuccess);
        if(linkSuccess == GL_FALSE) {
            GLchar messages[256];
            glGetProgramInfoLog(prog, sizeof(messages), 0, &messages[0]);
            NSString *messageString = [NSString stringWithUTF8String:messages];
            NSLog(@"%@", messageString);
            return NO;
        }
        
        return YES;
    }
    

    8.声明矩形四个角对应的数据

    //4个顶点(分别表示xyz轴)
    static const float Vertices[] = {
        
        -0.5, -0.5, 0,  //左下
         0.5, -0.5, 0,  //右下
        -0.5,  0.5, 0,  //左上
         0.5,  0.5, 0,  //右上
    };
    //4个点的颜色(分别表示RGBA值)
    static const float Colors[] = {
        1,0,0,1,
        0,1,0,1,
        0,0,1,1,
        0,0,0,1,
    };
    

    9.开始渲染

    - (void)render:(CADisplayLink *)displayLink {
        
        //用指定的颜色清除,清除颜色被设置为(0.5f, 0.5f, 0.5f, 1.0f), 所以为黑色
        glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);
        
        //设定窗口的范围(如果不是很明白, 可以自己动手修改下试试)
        //他这个是左下角为(0,0) 右上角为(width,height)
        glViewport(0, 0, _width, _height);
        
        //指定了渲染时索引值为 index 的顶点属性数组的数据格式和位置。
        /*
         *  indx:指定要修改的顶点属性的索引值
         *  size:指定每个顶点属性的组件数量。(必须坐标xyz轴就是3, 颜色rgba就是4)
         *  type:指定数组中每个组件的数据类型。(一般为GL_FLOAT)
         *  normalized:一般为GL_FALSE
         *  stride:指定连续顶点属性之间的偏移量。如果为0,那么顶点属性会被理解为:它们是紧密排列在一起的。初始值为0。
         *  ptr:指向数据的指针
         */
        glVertexAttribPointer(_positionSlot, 3, GL_FLOAT, GL_FALSE, 0, Vertices);
        glVertexAttribPointer(_colorSlot,    4, GL_FLOAT, GL_FALSE, 0, Colors);
        
     
        glDrawArrays(GL_TRIANGLE_STRIP, 0, sizeof(Vertices) / (sizeof(int) * 3));
        
        //把缓冲区的数据呈现到UIView上
        [_context presentRenderbuffer:GL_RENDERBUFFER];
    }
    
    

    glDrawArrays的第一个属性有如下图三种绘制方式.
    我们例子中用到的是GL_TRIANGLE_STRIP顺序是(左下右下左上右上).
    当然我们也可以用GL_TRIANGLE_FAN,但是顺序需要修改为(左下右下右上左上)这种方式,
    第一种GL_TRIANGLES表示没玩过~(> _ <)~. 原文链接

    10.释放资源

    - (void)dealloc {
        
        //删除绑定的渲染缓冲区
        if(_renderBuffer) {
            glDeleteRenderbuffers(GL_RENDERBUFFER, &_renderBuffer);
        }
        
        //删除绑定的帧缓冲区
        if(_frameBuffer) {
            glDeleteFramebuffers(GL_FRAMEBUFFER, &_frameBuffer);
        }
        
        //释放着色器
        if(_vertexShader) {
            
            //删除顶点着色器连接
            glDetachShader(_program, _vertexShader);
            
            //删除顶点着色器
            glDeleteShader(_vertexShader);
        }
        
        if(_fragmentShader) {
            
            //删除片段着色器连接
            glDetachShader(_program, _fragmentShader);
            
            //删除片段着色器
            glDeleteShader(_fragmentShader);
        }
        
        if(_program) {
            glDeleteProgram(_program);
        }
    }
    
    • 自定义OpenGLView代码就是这么多,不过里面有很多代码都是固定的.里面有很多固定的代码我都写在了一个父类里面了.在这里献上Demo.

    • 我自己测试的时候发现这个Demo里面有资源没释放,但是找不到,求大神。
    • 推荐一本书<<OpenGL ES 3.0 编程指南>>这本书相当于字典一样,所用到的函数都会讲解

    • 推荐一个学习OpenGL ES的网站, 后面我也会跟大家一起做里面的例子.

    相关文章

      网友评论

      • RM_乾笙:你推荐的网站是openGL而非openGLES,但一般入门都是openGL,才能搞openGLES,对吧,大神
        RM_乾笙:@Justin910 openGLES的详细讲解与用法比较少,但openGL的比较多
        Justin910:@张乾笙 OpenGL的学习资源相对比较多
        Justin910:@张乾笙 里面基本逻辑都是差不多的,并不是得先会OpenGL才能搞OpenGLES
      • 963e27e482f4:群友前排点赞~
        木马sun:群友二排点赞
        Justin910:@雷猩猩 谢谢支持

      本文标题:OpenGLES学习之路-矩形

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