iOS之OpenGL ES【2】:渲染管线和着色器

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Forever_wj/article/details/58151706

OpenGL ES的着色器主要有片元着色器和顶点着色器,其使用方法简单的介绍如下:

创建,装载和编译 shader

  • 继续上一篇的基础上,在工程中新建一个类,命名为OpenGLESUtils(继承于NSObject),在.h中声明2个方法:
 + (GLuint)loadShader:(GLenum)type shaderString:(NSString *)shaderString;

 + (GLuint)loadShader:(GLenum)type shaderFilePath:(NSString *)filePath;
  • 在OpenGLESUtils.m实现上面的两个方法:
+(GLuint)loadShader:(GLenum)type shaderFilePath:(NSString *)filePath {
    NSError *error;
    NSString *shaderString = [NSString stringWithContentsOfFile:filePath
                                                       encoding:NSUTF8StringEncoding
                                                          error:&error];
    if (!shaderString) {
        NSLog(@"Error: loading shader file: %@ %@", filePath, error.localizedDescription);
        return 0;
    } else {
        return [self loadShader:type shaderString:shaderString];
    }
}

 + (GLuint)loadShader:(GLenum)type shaderString:(NSString *)shaderString {
    GLuint shader = glCreateShader(type);
    if (shader == 0) {
        NSLog(@"Error: failed to create shader.");
        return 0;
    }
    const char *shaderStringUTF8 = [shaderString UTF8String];
    glShaderSource(shader, 1, &shaderStringUTF8, NULL);
    glCompileShader(shader);
    GLint compiled = 0;
    glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
    if (!compiled) {
        GLint inforLen = 0;
        glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &inforLen);
        if (inforLen > 1) {
            char *infoLog = malloc(sizeof(char)*inforLen);
            glGetShaderInfoLog(shader, inforLen, NULL, infoLog);
            free(infoLog);
        }
        glDeleteShader(shader);
        return 0;
    }
    return shader;
}
  • OpenGLESUtils中的两个方法用来跟进shader脚本字符串和shader脚本文件创建,具体方法如下:
    1、创建/删除 shader:函数 glCreateShader 用来创建 shader,参数 GLenum type 表示我们要处理的 shader 类型,它可以是 GL_VERTEX_SHADER 或 GL_FRAGMENT_SHADER,分别表示顶点 shader 或 片元 shader。它返回一个句柄指向创建好的 shader 对象;
    2、装载 shader:函数 glShaderSource 用来给指定 shader 提供 shader 源码。第一个参数是 shader 对象的句柄;第二个参数表示 shader 源码字符串的个数;第三个参数是 shader 源码字符串数组;第四个参数一个 int 数组,表示每个源码字符串应该取用的长度,如果该参数为 NULL,表示假定源码字符串是 \0 结尾的,读取该字符串的内容指定 \0 为止作为源码,如果该参数不是 NULL,则读取每个源码字符串中前 length(与每个字符串对应的 length)长度个字符作为源码;
    3、编译 shader:函数 glCompileShader 用来编译指定的 shader 对象,这将编译存储在 shader 对象中的源码。我们可以通过函数 glGetShaderiv 来查询 shader 对象的信息,如本例中查询编译情况,此外还可以查询 GL_DELETE_STATUS,GL_INFO_LOG_STATUS,GL_SHADER_SOURCE_LENGTH 和 GL_SHADER_TYPE。在这里我们查询编译情况,如果返回 0,表示编译出错了,错误信息会写入 info 日志中,我们可以查询该 info 日志,从而获得错误信息。

编写着色脚本

  • 添加顶点着色脚本:在工程中新建一个Empty,命名为VertexShader.glsl,实现内容如下:
attribute vec4 vPosition;

void main(void) {
    gl_Position = vPosition;
}
  • 添加片元着色脚本:同理实现,代码如下:
precision mediump float;

void main() {
    gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
  • 创建 program,装配 shader,链接 program,使用 program
    1、在 OpenGLView.h 的 OpenGLView 类声明中添加两个成员:
    GLuint programHandle;
    GLuint positionSlot;
    2、然后依然在 OpenGLView.m 中的category 中添加成员方法:
 - (void)setupProgram {
    NSString *vertexShaderPath = [[NSBundle mainBundle] pathForResource:@"VertexShader" ofType:@"glsl"];
    NSString *fragmentShaderPath = [[NSBundle mainBundle] pathForResource:@"FragmentShader" ofType:@"glsl"];
    GLuint vertexShader = [OpenGLESUtils loadShader:GL_VERTEX_SHADER shaderFilePath:vertexShaderPath];
    GLuint fragmentShader =[OpenGLESUtils loadShader:GL_FRAGMENT_SHADER shaderFilePath:fragmentShaderPath];
    programHandle = glCreateProgram();
    if (!programHandle) {
        NSLog(@"Failed to create program.");
        return;
    }

    glAttachShader(programHandle, vertexShader);
    glAttachShader(programHandle, fragmentShader);
    glLinkProgram(programHandle);

    GLint linked;
    glGetProgramiv(programHandle, GL_LINK_STATUS, &linked);
    if (!linked) {
        GLint infoLen = 0;glGetProgramiv(programHandle, GL_INFO_LOG_LENGTH, &infoLen);
        if (infoLen > 1) {
            char *infoLog = malloc(sizeof(char) *infoLen);
            glGetShaderInfoLog(programHandle, infoLen, NULL, infoLog);
            NSLog(@"Error linking program:\n%s\n", infoLog );
            free(infoLog);
        }
        glDeleteProgram(programHandle);
        programHandle = 0;
        return;
    }

    glUseProgram(programHandle);
    positionSlot = glGetAttribLocation(programHandle, "vPosition");
}
  • 使用实例:
    在 - (void)layoutSubviews中调用 render 方法之前,插入对 setupProgram 的调用:
- (void)layoutSubviews {
    [self setupLayer];
    [self setupContext];
    [self destoryRenderAndFrameBuffer];
    [self setupRenderBuffer];
    [self setupFrameBuffer];
    [self setupProgram];
    [self render];
}

// 改写render方法:
- (void)render {
    // 设置清屏颜色
    glClearColor(0.0, 0.0, 1.0, 1.0);
    // 用来指定要用清屏颜色来清除由mask指定的buffer,此处是color buffer
    glClear(GL_COLOR_BUFFER_BIT);

    // 渲染区域
    glViewport(0, 0, self.frame.size.width, self.frame.size.height);
    GLfloat vertices[] = {
        0.0f,  0.5f, 0.0f,
        -0.5f, -0.5f, 0.0f,
        0.5f,  -0.5f, 0.0f
    };

    // 通过 glVertexAttribPointer 将三角形顶点数据装载到 OpenGL ES 中并与 vPositon 关联起来
    glVertexAttribPointer(positionSlot, 3, GL_FLOAT, GL_FALSE, 0, vertices);
    glEnableVertexAttribArray(positionSlot);

    // glDrawArrays 将三角形图元渲染出来
    glDrawArrays(GL_TRIANGLES, 0, 3);
    [eaglContext presentRenderbuffer:GL_RENDERBUFFER    ];
}

参考文章:http://blog.csdn.net/kesalin/article/details/8223649

猜你喜欢

转载自blog.csdn.net/Forever_wj/article/details/58151706