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


上一篇我們學習了如何搭建IOS下openGL的開發環境,接下來我們來學習如何繪制一個多邊形。

在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;
    }

 

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

 

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

 
         

 

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

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

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM