zoukankan      html  css  js  c++  java
  • IOS 中openGL使用教程2(openGL ES 入门篇 | 绘制一个多边形)

    上一篇我们学习了如何搭建IOS下openGL的开发环境,接下来我们来学习如何绘制一个多边形。

    在2.0之前,es的渲染采用的是固定管线,何为固定管线,就是一套固定的模板流程,局部坐标变换 -> 世界坐标变换 ->观察坐标变换->背面消除->光照->裁剪->投影->视口计算->光栅化,程序员只需要调用固定的api修改一些配置参数就可以完成整个渲染流程了。而到了2.0,固定管线改成了可编程管线,我们对整个渲染流程可以再编程,没有固定的api给你调用,一切都依靠shader来完成。那么什么是shader呢:

    Shader分为顶点着色器(Vertex Shader)和片段着色器(Fragment Shader)。其中Vertex Shader主要负责顶点的几何关系等的运算,Pixel Shader主要负责片源颜色等的计算。
     
    我们使用的是openGLES2.0。因此必然会用到shader,shader使用OpenGL着色语言(GLSL―OpenGL Shading Language)编写,在这里就不仔细介绍。
     
     
    首先,设置好缓存
     GLuint _renderBuffer;
     GLuint _frameBuffer;
    
      glGenRenderbuffers(1, &_renderBuffer);
      glBindRenderbuffer(GL_RENDERBUFFER, _renderBuffer);
      [_eaglContext renderbufferStorage:GL_RENDERBUFFER fromDrawable:  (id<EAGLDrawable>)self.layer];
        
      glGenFramebuffers(1, &_frameBuffer);
      glBindFramebuffer(GL_FRAMEBUFFER, _frameBuffer);
      glFramebufferRenderbuffer(GL_FRAMEBUFFER,
                                  GL_COLOR_ATTACHMENT0,
                                  GL_RENDERBUFFER,
                                  _renderBuffer);
        

    在设置缓存之前,最好先清理一次,避免重复设置

    if (_frameBuffer) {
            glDeleteFramebuffers(1, &_frameBuffer);
            _frameBuffer = 0;
        }
        
        if (_renderBuffer) {
            glDeleteRenderbuffers(1, &_renderBuffer);
            _renderBuffer = 0;
        }

    设置好缓存之后开始编译着色器,基本上编译着色器的步骤都是固定,图像处理,滤镜实现基本都是通过着色器来完成。以下是编译着色器的代码。

    GLuint _program;
    GLunit _positionSlot;
    _program = [MTShaderOperations compileShaders:@"Vertex"
                                       shaderFragment:@"Fragment"];
     glUseProgram(_program);
      _positionSlot = glGetAttribLocation(_program, "Position");

     glUseProgram(_program);

    //绑定着色器中的参数

      _positionSlot = glGetAttribLocation(_program, "Position");

    + (GLuint)compileShaders:(NSString *)shaderVertex shaderFragment:(NSString *)shaderFragment {
        // 1 vertex和fragment两个shader都要编译
        GLuint vertexShader = [MTShaderOperations compileShader:shaderVertex withType:GL_VERTEX_SHADER];
        GLuint fragmentShader = [MTShaderOperations compileShader:shaderFragment withType:GL_FRAGMENT_SHADER];
        
        // 2 连接vertex和fragment shader成一个完整的program
        GLuint _glProgram = glCreateProgram();
        glAttachShader(_glProgram, vertexShader);
        glAttachShader(_glProgram, fragmentShader);
        
        // link program
        glLinkProgram(_glProgram);
        
        // 3 check link status
        GLint linkSuccess;
        glGetProgramiv(_glProgram, GL_LINK_STATUS, &linkSuccess);
        if (linkSuccess == GL_FALSE) {
            GLchar messages[256];
            glGetProgramInfoLog(_glProgram, sizeof(messages), 0, &messages[0]);
            NSString *messageString = [NSString stringWithUTF8String:messages];
            NSLog(@"%@", messageString);
            exit(1);
        }
        return _glProgram;
    }
    + (GLuint)compileShader:(NSString*)shaderName withType:(GLenum)shaderType {
        // 1 查找shader文件
        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);
        }
        
        // 2 创建一个代表shader的OpenGL对象, 指定vertex或fragment shader
        GLuint shaderHandle = glCreateShader(shaderType);
        
        // 3 获取shader的source
        const char* shaderStringUTF8 = [shaderString UTF8String];
        int shaderStringLength = (int)[shaderString length];
        glShaderSource(shaderHandle, 1, &shaderStringUTF8, &shaderStringLength);
        
        // 4 编译shader
        glCompileShader(shaderHandle);
        
        // 5 查询shader对象的信息
        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);
            exit(1);
        }
        
        return shaderHandle;
    }

    当然,要编译着色器,首先我们要写好着色器代码。绘制多边形的着色器代码十分简单,如果有不懂得童鞋可以参考OpenGL着色语言(GLSL―OpenGL Shading Language)

    新建一个empty文件,然后将后缀改成glsh就可以开始写着色器代码了。

    Vertex Shader代码如下:

    attribute vec4 Position;
    
    
    void main(void)
    {
        gl_Position = Position;
        
    }

    Fragment Shader代码如下

    void main(void)
    {
        
        gl_FragColor = vec4(0,1,1, 1.0);
    }

    编译好着色器之后,我们就可以开始绘制图形了,在这里要注意的是在openGLES中只能画三角形,所有的图形都是由三角形组成的。

    还有,在定义顶点时要注意openGL中的坐标系不同于UIKit。

    首先定义顶点数组

     const GLfloat vertices[] = {
            -1, -1, 0,   //左下
            1,  -1, 0,   //右下
            -1, 1,  0,   //左上
            1,  1,  0 }; //右上

    绘制图形

    //传入顶点参数 

    glVertexAttribPointer(_positionSlot, 3, GL_FLOAT, GL_FALSE, 0, vertices);

    glEnableVertexAttribArray(_positionSlot);

    //绘制多边形

     glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

    在上面我们画出了多边形,这个多边形是顶点数组按照顺序画出所有的三角形,在不少情况下,并不能符合我们的要求。我们可以试着将上面最后一句改成

    glDrawArrays(GL_TRIANGLE_STRIP, 1, 4);

    得到的图像如下

                          

    由于openGL只能画三角形,因此在顶点数组中,顶点按照怎样的顺序链接十分重要。因此在openGLES中提供了另一个方法。

    //定义索引数组
    const GLubyte indices[] = { 0,1,2, 1,2,3 };

    //根据索引数组的顺序画出多边形

     glDrawElements(GL_TRIANGLES, sizeof(indices)/sizeof(indices[0]), GL_UNSIGNED_BYTE, indices);

    
    

    这样我们就画出了一个四边形,其他多边形也是一样的步骤。

    下一篇章我们会讲如何传入一个纹理,敬请期待。

  • 相关阅读:
    ios录音
    ios 音乐播放
    ios程序播放音频文件
    ios摇一摇截屏代码
    加速计
    蓝牙4.0实现及原理
    物理仿真
    iOS 监听耳机变化
    删除缓存
    宏定义加参数
  • 原文地址:https://www.cnblogs.com/bigly/p/5804541.html
Copyright © 2011-2022 走看看