iOS下OpenGL ES 3.0编程入门(二 ):画一个简单三角形

时间:2022-09-07 09:24:34

       上文我们讲解了如何构建一个hello world开发环境,那么这一篇我们就来画一个简单的三角形出来。

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

Shader分为Vertex Shader顶点着色器和Pixel Shader像素着色器两种。其中Vertex Shader主要负责顶点的几何关系等的运算,Pixel Shader主要负责片源颜色等的计算。
着色器替代了传统的固定渲染管线,可以实现3D图形学计算中的相关计算,由于其可编辑性,可以实现各种各样的图像效果而不用受显卡的固定渲染管线限制。这极大的提高了图像的画质。
好了,介绍完渲染流程,我们变进入正题,如何在上一篇的基础上完成一个简单三角形的渲染呢?
首先,我们要建立一个三角形的局部坐标系,也就是三角形每个点的顶点坐标。
GLKVector3 vec[3]={
    {0.5,0.5,0.5},
    {-0.5,-0.5,0.5},
    {0.5,-0.5,-0.5}
};
        然后,因为我们介绍的是opengl es3.0的编程,所以我们用的是可编程管线,那么我们就应该建立一个vertex shader文件和一个pixel shader文件,分别命名为shader.vsh和shader.fsh。
shader.vsh:
attribute vec3 position;   //入参,主程序会将数值传入
void main()
{
    gl_Position = vec4(position,1);  //顶点经过投影变换变换后的位置
}
        shader.fsh:
void main()
{
    gl_FragColor = vec4(0.5,0.5,0.5,1);   //顶点的颜色
}
       可以看到,每一个着色器文件都一个类似于c语言的main函数入口,这个就是着色器的入口,所有的代码都从这里开始执行。
编写完着色器后,我们便需要在主程序里加载shader了,加载shader的代码基本上不需要变动什么,直接copy过来就可以了。
- (BOOL)loadShaders
{
    GLuint vertShader, fragShader;
    NSString *vertShaderPathname, *fragShaderPathname;
    
    // Create shader program.
    program = glCreateProgram();
    
    // Create and compile vertex shader.
    vertShaderPathname = [[NSBundle mainBundle] pathForResource:@"Shader" ofType:@"vsh"];
    if (![self compileShader:&vertShader type:GL_VERTEX_SHADER file:vertShaderPathname]) {
        NSLog(@"Failed to compile vertex shader");
        return NO;
    }
    
    // Create and compile fragment shader.
    fragShaderPathname = [[NSBundle mainBundle] pathForResource:@"Shader" ofType:@"fsh"];
    if (![self compileShader:&fragShader type:GL_FRAGMENT_SHADER file:fragShaderPathname]) {
        NSLog(@"Failed to compile fragment shader");
        return NO;
    }
    
    // Attach vertex shader to program.
    glAttachShader(program, vertShader);
    
    // Attach fragment shader to program.
    glAttachShader(program, fragShader);
    
    // Link program.
    if (![self linkProgram:program]) {
        NSLog(@"Failed to link program: %d", program);
        
        if (vertShader) {
            glDeleteShader(vertShader);
            vertShader = 0;
        }
        if (fragShader) {
            glDeleteShader(fragShader);
            fragShader = 0;
        }
        if (program) {
            glDeleteProgram(program);
            program = 0;
        }
        
        return NO;
    }
    // Release vertex and fragment shaders.
    if (vertShader) {
        glDetachShader(program, vertShader);
        glDeleteShader(vertShader);
    }
    if (fragShader) {
        glDetachShader(program, fragShader);
        glDeleteShader(fragShader);
    }
    
    return YES;
}

- (BOOL)compileShader:(GLuint *)shader type:(GLenum)type file:(NSString *)file
{
    GLint status;
    const GLchar *source;
    
    source = (GLchar *)[[NSString stringWithContentsOfFile:file encoding:NSUTF8StringEncoding error:nil] UTF8String];
    if (!source) {
        NSLog(@"Failed to load vertex shader");
        return NO;
    }
    
    *shader = glCreateShader(type);
    glShaderSource(*shader, 1, &source, NULL);
    glCompileShader(*shader);
    
#if defined(DEBUG)
    GLint logLength;
    glGetShaderiv(*shader, GL_INFO_LOG_LENGTH, &logLength);
    if (logLength > 0) {
        GLchar *log = (GLchar *)malloc(logLength);
        glGetShaderInfoLog(*shader, logLength, &logLength, log);
        NSLog(@"Shader compile log:\n%s", log);
        free(log);
    }
#endif
    
    glGetShaderiv(*shader, GL_COMPILE_STATUS, &status);
    if (status == 0) {
        glDeleteShader(*shader);
        return NO;
    }
    
    return YES;
}

- (BOOL)linkProgram:(GLuint)prog
{
    GLint status;
    glLinkProgram(prog);
    
#if defined(DEBUG)
    GLint logLength;
    glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &logLength);
    if (logLength > 0) {
        GLchar *log = (GLchar *)malloc(logLength);
        glGetProgramInfoLog(prog, logLength, &logLength, log);
        NSLog(@"Program link log:\n%s", log);
        free(log);
    }
#endif
    
    glGetProgramiv(prog, GL_LINK_STATUS, &status);
    if (status == 0) {
        return NO;
    }
    
    return YES;
}

- (BOOL)validateProgram:(GLuint)prog
{
    GLint logLength, status;
    
    glValidateProgram(prog);
    glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &logLength);
    if (logLength > 0) {
        GLchar *log = (GLchar *)malloc(logLength);
        glGetProgramInfoLog(prog, logLength, &logLength, log);
        NSLog(@"Program validate log:\n%s", log);
        free(log);
    }
    
    glGetProgramiv(prog, GL_VALIDATE_STATUS, &status);
    if (status == 0) {
        return NO;
    }
    
    return YES;
}
      然后再viewdidload里面添加如下代码:
[self loadShaders];
    glEnable(GL_DEPTH_TEST);
    glClearColor(0.1, 0.2, 0.3, 1);
    glGenVertexArrays(1, &vertexID);//生成一个vao对象
    glBindVertexArray(vertexID); //绑定vao
    GLuint bufferID;
    glGenBuffers(1, &bufferID);  //生成vbo
glBindBuffer(GL_ARRAY_BUFFER, bufferID);  //绑定
    glBufferData(GL_ARRAY_BUFFER, sizeof(vec), vec, GL_STATIC_DRAW); //填充缓冲对象
    GLuint loc=glGetAttribLocation(program, "position");   //获得shader里position变量的索引
    glEnableVertexAttribArray(loc);     //启用这个索引
    glVertexAttribPointer(loc, 3, GL_FLOAT, GL_FALSE, sizeof(GLKVector3), 0);  //设置这个索引需要填充的内容
    glBindVertexArray(0);   //释放vao
    glBindBuffer(GL_ARRAY_BUFFER, 0);  //释放vbo
        这里我们用到了es 3.0里面的新技术vao(vertex array object)以及2.0里面的vbo。关于vao和vbo,我们会专门展开一章来探讨,现在大家知道就行啦。
       接下来便到了渲染阶段,这里的代码需要写在-(void)glkView:(GLKView *)view drawInRect:(CGRect)rect方法里。
-(void)glkView:(GLKView *)view drawInRect:(CGRect)rect
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);   //清除颜色缓冲和深度缓冲
    glBindVertexArray(vertexID);    
    glUseProgram(program);      //使用shader
    glDrawArrays(GL_TRIANGLES, 0, 3);     //绘制三角形
    glBindVertexArray(0);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
      代码阶段便编写完成啦,保存运行,出现如下界面便大功告成啦。

iOS下OpenGL ES 3.0编程入门(二 ):画一个简单三角形