IOS 中openGL使用教程2(openGL ES 入門篇 | 繪製一個多邊形)

上一篇咱們學習瞭如何搭建IOS下openGL的開發環境,接下來咱們來學習如何繪製一個多邊形。html

在2.0以前,es的渲染採用的是固定管線,何爲固定管線,就是一套固定的模板流程,局部座標變換 -> 世界座標變換 ->觀察座標變換->背面消除->光照->裁剪->投影->視口計算->光柵化,程序員只須要調用固定的api修改一些配置參數就能夠完成整個渲染流程了。而到了2.0,固定管線改爲了可編程管線,咱們對整個渲染流程能夠再編程,沒有固定的api給你調用,一切都依靠shader來完成。那麼什麼是shader呢:程序員

Shader分爲頂點着色器(Vertex Shader)和片斷着色器(Fragment Shader)。其中Vertex Shader主要負責頂點的幾何關係等的運算,Pixel Shader主要負責片源顏色等的計算。
 
咱們使用的是openGLES2.0。所以必然會用到shader,shader使用 OpenGL着色語言(GLSL―OpenGL Shading Language)編寫,在這裏就不仔細介紹。
 
 
首先,設置好緩存
 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;
    }

 

設置好緩存以後開始編譯着色器,基本上編譯着色器的步驟都是固定,圖像處理,濾鏡實現基本都是經過着色器來完成。如下是編譯着色器的代碼。api

 

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)ui

新建一個empty文件,而後將後綴改爲glsh就能夠開始寫着色器代碼了。spa

Vertex Shader代碼以下:3d

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);

 
 

 

這樣咱們就畫出了一個四邊形,其餘多邊形也是同樣的步驟。

下一篇章咱們會講如何傳入一個紋理,敬請期待。

相關文章
相關標籤/搜索