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 渲染流程如下图几个阶段:
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地址
参考:
你好,三角形