OpenGLES繪制三角形
最近工作需要用到OpenGL ES和GPUImage,在這里記錄一下自己學(xué)習(xí)到的知識(shí)
OpenGL ES是什么
OpenGL(Open Graphics Library)定義了一個(gè)跨編程語言、跨平臺(tái) 編程的專業(yè)圖形程序接口∏城牵可用于二維或三維圖像的處理與渲染设凹,它是 一個(gè)功能強(qiáng)大案站、調(diào)用方便的底層圖形庫痛黎。對(duì)于嵌入式的設(shè)備,其提供了 OpenGL ES(OpenGL for Embedded Systems)版本以躯,該版本是針對(duì)手 機(jī)槐秧、Pad等嵌入式設(shè)備而設(shè)計(jì)的啄踊,是OpenGL的一個(gè)子集。
OpenGL 渲染流程
在OpenGL中刁标,任何事物都在3D空間中颠通,而屏幕和窗口卻是2D像素?cái)?shù)組,這導(dǎo)致OpenGL的大部分工作都是關(guān)于把3D坐標(biāo)轉(zhuǎn)變?yōu)檫m應(yīng)你屏幕的2D像素膀懈。3D坐標(biāo)轉(zhuǎn)為2D坐標(biāo)的處理過程是由OpenGL的圖形渲染管線(Graphics Pipeline顿锰,大多譯為管線,實(shí)際上指的是一堆原始圖形數(shù)據(jù)途經(jīng)一個(gè)輸送管道启搂,期間經(jīng)過各種變化處理最終出現(xiàn)在屏幕的過程)管理的撵儿。
OpenGL 渲染流程如下圖幾個(gè)階段:
iOS下使用OpenGL ES 流程
1. 創(chuàng)建自定義View并修改Layer
在iOS中一般使用CAEAGLLayer來實(shí)現(xiàn)OpenGL的各種功能,一般使用自定義的UIView狐血,并改變他的layer的類型。CAEAGLLayer默認(rèn)是透明的易核,官方建議設(shè)為不透明匈织。
self.eagLayer = (CAEAGLLayer *)self.layer;
[self setContentScaleFactor:[[UIScreen mainScreen] scale]];
self.eagLayer.opaque = YES;
2. 創(chuàng)建Context上下文
EAGLContext *context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
if (![EAGLContext setCurrentContext:context]) {
NSLog(@"Fail to set current context");
}
self.context = context;
3. 創(chuàng)建渲染緩存(Render Buffer)
render buffer用來存儲(chǔ)即將繪制到屏幕上的圖像數(shù)據(jù),理解為幀緩沖的一個(gè)附件牡直,用來真正存儲(chǔ)圖像的數(shù)據(jù)缀匕。
// 創(chuàng)建幀緩沖區(qū)
glGenRenderbuffers(1, &_renderBuffer);
// 綁定幀緩沖區(qū)到渲染管線
glBindRenderbuffer(GL_RENDERBUFFER, self.renderBuffer);
// 為繪制緩沖區(qū)分配存儲(chǔ)區(qū):將CAEAGLLayer的繪制存儲(chǔ)區(qū)作為繪制緩沖區(qū)的存儲(chǔ)區(qū)
[self.context renderbufferStorage:GL_RENDERBUFFER fromDrawable:self.eagLayer];
4. 創(chuàng)建幀緩沖 (Frame Buffer)
幀緩沖理解為多種緩沖的結(jié)合。
// 創(chuàng)建繪制緩沖區(qū)
glGenFramebuffers(1, &_frameBuffer);
// 邦定繪制緩沖區(qū)到渲染管線
glBindFramebuffer(GL_FRAMEBUFFER, self.frameBuffer);
// 將繪制緩沖區(qū)邦定到幀緩沖區(qū)
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, self.renderBuffer);
5. 創(chuàng)建Shader
Shader使用著色器語言GLSL(OpenGL Shading Language)編寫碰逸,主要需要編寫頂點(diǎn)著色器和片段著色器的實(shí)現(xiàn)乡小,頂點(diǎn)著色器將計(jì)算好的頂點(diǎn)傳入片段著色器,然后片段著色器計(jì)算像素最后的顏色輸出饵史。
Xcode新建兩個(gè)文件:shader.vsh和shader.fsh满钟,分別代表頂點(diǎn)著色器和片元著色器
shader.vsh內(nèi)容如下:
//attribute 關(guān)鍵字用來描述傳入shader的變量
attribute vec4 position;
void main()
{
gl_Position = position; // gl_Position是vertex shader的內(nèi)建變量,gl_Position中的頂點(diǎn)值最終輸出到渲染管線中
}
shader.fsh內(nèi)容如下:
void main()
{
gl_FragColor = vec4(0.5, 0.5, 0.5, 1.0); // gl_FragColor是fragment shader的內(nèi)建變量胳喷,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) { //連接錯(cuò)誤
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. 傳入頂點(diǎn)數(shù)據(jù)湃番,繪制三角形
- (void)render {
//清屏為白色
glClearColor(1.0, 1.0, 1.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
CGFloat scale = [[UIScreen mainScreen] scale]; //獲取視圖放大倍數(shù),可以把scale設(shè)置為1試試
//設(shè)置gl渲染窗口大小
glViewport(self.frame.origin.x * scale, self.frame.origin.y * scale, self.frame.size.width * scale, self.frame.size.height * scale); //設(shè)置視口大小
//讀取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); //成功便使用吭露,避免由于未使用導(dǎo)致的的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);
//將頂點(diǎn)坐標(biāo)寫入頂點(diǎn)VBO
glBufferData(GL_ARRAY_BUFFER, sizeof(attrArr), attrArr, GL_STATIC_DRAW);
// 獲取參數(shù)索引
GLuint position = glGetAttribLocation(self.program, "position");
//告訴OpenGL該如何解析頂點(diǎn)數(shù)據(jù)
glVertexAttribPointer(position, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 3, NULL);
glEnableVertexAttribArray(position);
//繪制三個(gè)頂點(diǎn)的三角形
glDrawArrays(GL_TRIANGLES, 0, 3);
//EACAGLContext 渲染OpenGL繪制好的圖像到EACAGLLayer
[self.context presentRenderbuffer:GL_RENDERBUFFER];
}
這樣子運(yùn)行起來就可以看到一個(gè)灰色的三角形了
參考:
你好吠撮,三角形