在上一篇我們學習了如何搭建IOS下openGL的開發環境,接下來我們來學習如何繪制一個多邊形。
在2.0之前,es的渲染采用的是固定管線,何為固定管線,就是一套固定的模板流程,局部坐標變換 -> 世界坐標變換 ->觀察坐標變換->背面消除->光照->裁剪->投影->視口計算->光柵化,程序員只需要調用固定的api修改一些配置參數就可以完成整個渲染流程了。而到了2.0,固定管線改成了可編程管線,我們對整個渲染流程可以再編程,沒有固定的api給你調用,一切都依靠shader來完成。那么什么是shader呢:
GLuint _renderBuffer; GLuint _frameBuffer; glGenRenderbuffers(1, &_renderBuffer); glBindRenderbuffer(GL_RENDERBUFFER, _renderBuffer); [_eaglContext renderbufferStorage:GL_RENDERBUFFER fromDrawable: (id<EAGLDrawable>)self.layer]; glGenFramebuffers(1, &_frameBuffer); glBindFramebuffer(GL_FRAMEBUFFER, _frameBuffer); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, _renderBuffer);
在設置緩存之前,最好先清理一次,避免重復設置
if (_frameBuffer) { glDeleteFramebuffers(1, &_frameBuffer); _frameBuffer = 0; } if (_renderBuffer) { glDeleteRenderbuffers(1, &_renderBuffer); _renderBuffer = 0; }
設置好緩存之后開始編譯着色器,基本上編譯着色器的步驟都是固定,圖像處理,濾鏡實現基本都是通過着色器來完成。以下是編譯着色器的代碼。
GLuint _program; GLunit _positionSlot; _program = [MTShaderOperations compileShaders:@"Vertex" shaderFragment:@"Fragment"]; glUseProgram(_program); _positionSlot = glGetAttribLocation(_program, "Position");
glUseProgram(_program);
//綁定着色器中的參數
_positionSlot = glGetAttribLocation(_program, "Position");
+ (GLuint)compileShaders:(NSString *)shaderVertex shaderFragment:(NSString *)shaderFragment { // 1 vertex和fragment兩個shader都要編譯 GLuint vertexShader = [MTShaderOperations compileShader:shaderVertex withType:GL_VERTEX_SHADER]; GLuint fragmentShader = [MTShaderOperations compileShader:shaderFragment withType:GL_FRAGMENT_SHADER]; // 2 連接vertex和fragment shader成一個完整的program GLuint _glProgram = glCreateProgram(); glAttachShader(_glProgram, vertexShader); glAttachShader(_glProgram, fragmentShader); // link program glLinkProgram(_glProgram); // 3 check link status GLint linkSuccess; glGetProgramiv(_glProgram, GL_LINK_STATUS, &linkSuccess); if (linkSuccess == GL_FALSE) { GLchar messages[256]; glGetProgramInfoLog(_glProgram, sizeof(messages), 0, &messages[0]); NSString *messageString = [NSString stringWithUTF8String:messages]; NSLog(@"%@", messageString); exit(1); } return _glProgram; }
+ (GLuint)compileShader:(NSString*)shaderName withType:(GLenum)shaderType { // 1 查找shader文件 NSString* shaderPath = [[NSBundle mainBundle] pathForResource:shaderName ofType:@"glsl"]; NSError* error; NSString* shaderString = [NSString stringWithContentsOfFile:shaderPath encoding:NSUTF8StringEncoding error:&error]; if (!shaderString) { NSLog(@"Error loading shader: %@", error.localizedDescription); exit(1); } // 2 創建一個代表shader的OpenGL對象, 指定vertex或fragment shader GLuint shaderHandle = glCreateShader(shaderType); // 3 獲取shader的source const char* shaderStringUTF8 = [shaderString UTF8String]; int shaderStringLength = (int)[shaderString length]; glShaderSource(shaderHandle, 1, &shaderStringUTF8, &shaderStringLength); // 4 編譯shader glCompileShader(shaderHandle); // 5 查詢shader對象的信息 GLint compileSuccess; glGetShaderiv(shaderHandle, GL_COMPILE_STATUS, &compileSuccess); if (compileSuccess == GL_FALSE) { GLchar messages[256]; glGetShaderInfoLog(shaderHandle, sizeof(messages), 0, &messages[0]); NSString *messageString = [NSString stringWithUTF8String:messages]; NSLog(@"%@", messageString); exit(1); } return shaderHandle; }
當然,要編譯着色器,首先我們要寫好着色器代碼。繪制多邊形的着色器代碼十分簡單,如果有不懂得童鞋可以參考OpenGL着色語言(GLSL―OpenGL Shading Language)
新建一個empty文件,然后將后綴改成glsh就可以開始寫着色器代碼了。
Vertex Shader代碼如下:
attribute vec4 Position; void main(void) { gl_Position = Position; }
Fragment Shader代碼如下
void main(void) { gl_FragColor = vec4(0,1,1, 1.0); }
編譯好着色器之后,我們就可以開始繪制圖形了,在這里要注意的是在openGLES中只能畫三角形,所有的圖形都是由三角形組成的。
還有,在定義頂點時要注意openGL中的坐標系不同於UIKit。
首先定義頂點數組
const GLfloat vertices[] = { -1, -1, 0, //左下 1, -1, 0, //右下 -1, 1, 0, //左上 1, 1, 0 }; //右上
繪制圖形
//傳入頂點參數
glVertexAttribPointer(_positionSlot, 3, GL_FLOAT, GL_FALSE, 0, vertices);
glEnableVertexAttribArray(_positionSlot);
//繪制多邊形
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
在上面我們畫出了多邊形,這個多邊形是頂點數組按照順序畫出所有的三角形,在不少情況下,並不能符合我們的要求。我們可以試着將上面最后一句改成
glDrawArrays(GL_TRIANGLE_STRIP, 1, 4);
得到的圖像如下

由於openGL只能畫三角形,因此在頂點數組中,頂點按照怎樣的順序鏈接十分重要。因此在openGLES中提供了另一個方法。
//定義索引數組
const GLubyte indices[] = { 0,1,2, 1,2,3 };
//根據索引數組的順序畫出多邊形
glDrawElements(GL_TRIANGLES, sizeof(indices)/sizeof(indices[0]), GL_UNSIGNED_BYTE, indices);
這樣我們就畫出了一個四邊形,其他多邊形也是一樣的步驟。
下一篇章我們會講如何傳入一個紋理,敬請期待。
