IOS 中openGL使用教程2(openGL ES 入门篇 | 绘制一个多边形)

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

在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;
    }

 

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

 

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)ui

新建一个empty文件,而后将后缀改为glsh就能够开始写着色器代码了。spa

Vertex Shader代码以下:3d

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);

 
 

 

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

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

相关文章
相关标签/搜索