iOS OpenGL ES绘制三角形

OpenGLES绘制三角形

最近工作需要用到OpenGL ES和GPUImage,在这里记录一下自己学习到的知识

OpenGL ES是什么

OpenGL(Open Graphics Library)定义了一个跨编程语言、跨平台 编程的专业图形程序接口。可用于二维或三维图像的处理与渲染,它是 一个功能强大、调用方便的底层图形库。对于嵌入式的设备,其提供了 OpenGL ES(OpenGL for Embedded Systems)版本,该版本是针对手 机、Pad等嵌入式设备而设计的,是OpenGL的一个子集。

OpenGL 渲染流程

在OpenGL中,任何事物都在3D空间中,而屏幕和窗口却是2D像素数组,这导致OpenGL的大部分工作都是关于把3D坐标转变为适应你屏幕的2D像素。3D坐标转为2D坐标的处理过程是由OpenGL的图形渲染管线(Graphics Pipeline,大多译为管线,实际上指的是一堆原始图形数据途经一个输送管道,期间经过各种变化处理最终出现在屏幕的过程)管理的。

OpenGL 渲染流程如下图几个阶段:


pipeline.png

iOS下使用OpenGL ES 流程

1. 创建自定义View并修改Layer

在iOS中一般使用CAEAGLLayer来实现OpenGL的各种功能,一般使用自定义的UIView,并改变他的layer的类型。CAEAGLLayer默认是透明的,官方建议设为不透明。

self.eagLayer = (CAEAGLLayer *)self.layer;
[self setContentScaleFactor:[[UIScreen mainScreen] scale]];
self.eagLayer.opaque = YES;

2. 创建Context上下文

EAGLContext *context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
if (![EAGLContext setCurrentContext:context]) {
    NSLog(@"Fail to set current context");
}
self.context = context;

3. 创建渲染缓存(Render Buffer)

render buffer用来存储即将绘制到屏幕上的图像数据,理解为帧缓冲的一个附件,用来真正存储图像的数据。

// 创建帧缓冲区
glGenRenderbuffers(1, &_renderBuffer);
// 绑定帧缓冲区到渲染管线
glBindRenderbuffer(GL_RENDERBUFFER, self.renderBuffer);
// 为绘制缓冲区分配存储区:将CAEAGLLayer的绘制存储区作为绘制缓冲区的存储区
[self.context renderbufferStorage:GL_RENDERBUFFER fromDrawable:self.eagLayer];

4. 创建帧缓冲 (Frame Buffer)

帧缓冲理解为多种缓冲的结合。

// 创建绘制缓冲区
glGenFramebuffers(1, &_frameBuffer);
// 邦定绘制缓冲区到渲染管线
glBindFramebuffer(GL_FRAMEBUFFER, self.frameBuffer);
// 将绘制缓冲区邦定到帧缓冲区
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, self.renderBuffer);

5. 创建Shader

Shader使用着色器语言GLSL(OpenGL Shading Language)编写,主要需要编写顶点着色器和片段着色器的实现,顶点着色器将计算好的顶点传入片段着色器,然后片段着色器计算像素最后的颜色输出。

Xcode新建两个文件:shader.vsh和shader.fsh,分别代表顶点着色器和片元着色器

shader.vsh内容如下:

//attribute 关键字用来描述传入shader的变量
attribute vec4 position;

void main()
{
    gl_Position = position; // gl_Position是vertex shader的内建变量,gl_Position中的顶点值最终输出到渲染管线中
}

shader.fsh内容如下:

 void main()
{
    gl_FragColor = vec4(0.5, 0.5, 0.5, 1.0); // gl_FragColor是fragment shader的内建变量,gl_FragColor中的像素值最终输出到渲染管线中
}

6. 编译和链接Shader并链接到着色器程序

- (BOOL)buildProgramWithShaders:(NSString *)vert frag:(NSString *)frag {
    GLuint verShader, fragShader;
    self.program = glCreateProgram();
    
    //编译
    [self compileShader:&verShader type:GL_VERTEX_SHADER file:vert];
    [self compileShader:&fragShader type:GL_FRAGMENT_SHADER file:frag];
    
    glAttachShader(self.program, verShader);
    glAttachShader(self.program, fragShader);
    
    //释放不需要的shader
    glDeleteShader(verShader);
    glDeleteShader(fragShader);
    
    //链接
    glLinkProgram(self.program);
    GLint linkSuccess;
    glGetProgramiv(self.program, GL_LINK_STATUS, &linkSuccess);
    if (linkSuccess == GL_FALSE) { //连接错误
        GLchar messages[256];
        glGetProgramInfoLog(self.program, sizeof(messages), 0, &messages[0]);
        NSString *messageString = [NSString stringWithUTF8String:messages];
        NSLog(@"error%@", messageString);
        return NO;
    }
    
    return YES;
}

- (void)compileShader:(GLuint *)shader type:(GLenum)type file:(NSString *)file {
    NSString *content = [NSString stringWithContentsOfFile:file encoding:NSUTF8StringEncoding error:nil];
    const GLchar *source = [content UTF8String];
    
    *shader = glCreateShader(type);
    
    glShaderSource(*shader, 1, &source, NULL);
    
    glCompileShader(*shader);
}

7. 传入顶点数据,绘制三角形

- (void)render {
    //清屏为白色
    glClearColor(1.0, 1.0, 1.0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT);
    
    CGFloat scale = [[UIScreen mainScreen] scale]; //获取视图放大倍数,可以把scale设置为1试试
    //设置gl渲染窗口大小
    glViewport(self.frame.origin.x * scale, self.frame.origin.y * scale, self.frame.size.width * scale, self.frame.size.height * scale); //设置视口大小
    
    //读取shader文件路径
    NSString* vertFile = [[NSBundle mainBundle] pathForResource:@"shader" ofType:@"vsh"];
    NSString* fragFile = [[NSBundle mainBundle] pathForResource:@"shader" ofType:@"fsh"];
    
    BOOL buildProgramSuccess = [self buildProgramWithShaders:vertFile frag:fragFile];
    if (!buildProgramSuccess) {
        return;
    }
    glUseProgram(self.program); //成功便使用,避免由于未使用导致的的bug
    
    GLfloat attrArr[] =
    {
        1.0f, -0.5f, 0.0f,  // 右下
        0.0f, 0.5f, 0.0f,   // 上
        -1.0f, -0.5f, 0.0f  // 左下
    };
    
    GLuint attrBuffer;
    glGenBuffers(1, &attrBuffer);
    glBindBuffer(GL_ARRAY_BUFFER, attrBuffer);
    //将顶点坐标写入顶点VBO
    glBufferData(GL_ARRAY_BUFFER, sizeof(attrArr), attrArr, GL_STATIC_DRAW);
    
    // 获取参数索引
    GLuint position = glGetAttribLocation(self.program, "position");
    
    //告诉OpenGL该如何解析顶点数据
    glVertexAttribPointer(position, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 3, NULL);
    glEnableVertexAttribArray(position);
    
    //绘制三个顶点的三角形
    glDrawArrays(GL_TRIANGLES, 0, 3);
    
    //EACAGLContext 渲染OpenGL绘制好的图像到EACAGLLayer
    [self.context presentRenderbuffer:GL_RENDERBUFFER];
}

这样子运行起来就可以看到一个灰色的三角形了

demo地址

参考:
你好,三角形

你可能感兴趣的:(iOS OpenGL ES绘制三角形)