OpenGLES學習之路-矩形

前言

  • 因為在書上和網上看到說寫Blog各種好, 所以就來嘗試一下,雖然不會有人看,但是沒關系畢竟可以鞏固和更加深入的了解.
  • 我公司所處的行業是安防監控, 所以剛好有機會接觸音視頻相關的內容.雖然我是個菜鳥, 但是菜鳥也會有春天的O(∩_∩)O哈哈~.
  • 在這里跟大家一起學習OpenGLES,一起進步.
  • 本系列不會講太多的理論知識, 會講解盡量多的用法, 畢竟了解更多的用法在實際使用中可以產生更多想法.

最終效果

實現流程

  • 1.設置上下文
  • 2.設置Layer
  • 3.設置渲染緩沖區
  • 4.設置幀緩沖區
  • 5.創建頂點著色器和片段著色器
  • 6.編譯著色器
  • 7.鏈接著色器
  • 8.聲明矩形四個角對應的數據
  • 9.開始渲染
  • 10.釋放資源

自定義的OpenGLView有點小復雜, 實現過程比系統提供的GLKView復雜了一丟丟.但是可以更深入的了解OpenGLES實現過程

實現過程

1.設置上下文

/**
 *  設置上下文
 */
- (void)setupContext {
    
    _context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];
    if (!_context) {
        
        _context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
    }
    
    if (![EAGLContext setCurrentContext:_context]) {
        NSLog(@"Failed to set current OpenGL context");
        exit(1);
    }
}

2.設置Layer

+(Class)layerClass {
    return [CAEAGLLayer class];
}

- (void)setupLayer {
    _eaglLayer = (CAEAGLLayer*)self.layer;
    _eaglLayer.opaque = YES;
}

3.設置渲染緩沖區

/**
 *  創建一個渲染緩沖
 */
- (void)setupRenderBuffer {
    
    glGenRenderbuffers(1, &_renderBuffer);
    glBindRenderbuffer(GL_RENDERBUFFER, _renderBuffer);
    [_context renderbufferStorage:GL_RENDERBUFFER fromDrawable:_eaglLayer];
}

4.設置幀緩沖區

/**
 *  設置幀緩沖區
 */
- (void)setupFrameBuffer {
    
    glGenFramebuffers(1, &_frameBuffer);
    glBindFramebuffer(GL_FRAMEBUFFER, _frameBuffer);
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, _renderBuffer);
}

5.創建頂點著色器和片段著色器

(1)創建頂點著色器RectangleVertex.glsl

attribute vec4 Position;

attribute vec4 InColor;
varying vec4 OutColor;

void main(void){
    OutColor = InColor;
    gl_Position = Position;
}

/*
    vec2.vec3.vec4.表示對應的234階矩陣
    
    attribute修飾符表示從"應用程序"傳過來的數據(也就是下面這些數據傳過來)
     //4個頂點(分別表示xyz軸)
     static const float Vertices[] = {
     
         -0.5, -0.5, 0,  //左下
          0.5, -0.5, 0,  //右下
         -0.5,  0.5, 0,  //左上
          0.5,  0.5, 0,  //右上
     };
     
     //4個點的顏色(分別表示RGBA值)
     static const float Colors[] = {
         
         1,0,0,1,
         0,1,0,1,
         0,0,1,1,
         0,0,0,1,
     };
 
    varying修飾符是用于和片段著色器通訊的接口
 */

(2)創建片段著色器RectangleFragment.glsl

varying lowp vec4 OutColor;

void main(void){
    gl_FragColor = OutColor;
}

上面的這些修飾符可以在這里看.

6.編譯著色器

- (GLuint)compileShader:(NSString*)shaderName withType:(GLenum)shaderType {
    
    // 獲取資源路徑
    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);
    }
    
    /*
     *  GL_FRAGMENT_SHADER  創建一個片段著色器
     *  GL_VERTEX_SHADER    創建一個頂點著色器
     */
    GLuint shaderHandle          = glCreateShader(shaderType);
    
    // 轉換成char*類型(給予OpenGL著色器)
    const char *shaderStringUTF8 = [shaderString UTF8String];
    int shaderStringLength       = (int)[shaderString length];
    
    /**
     *  @param shader      所產生的著色器名稱
     *  @param count       表示多少資源傳遞一次(如果只上傳一個著色代碼,這里必須填1)
     *  @param string      C語言的資源路徑
     *  @param length      C語言資源路徑的字符長度
     */
    glShaderSource(shaderHandle, 1, &shaderStringUTF8, &shaderStringLength);
    
    // 調用運行時編譯的著色器
    glCompileShader(shaderHandle);
    
    // 查看是否有錯誤,有的話獲取錯誤信息
    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);
        glDeleteShader(shaderHandle);
        exit(1);
    }
    
    return shaderHandle;
}

7.鏈接著色器

/**
 *  編譯著色器
 */
- (void)compileShaders {
    
    //編譯頂點著色器和片段著色器
    GLuint vertexShader   = [self compileShader:@"RectangleVertex" withType:GL_VERTEX_SHADER];
    GLuint fragmentShader = [self compileShader:@"RectangleFragment" withType:GL_FRAGMENT_SHADER];
    
    //把頂點和片段著色器鏈接到一個完整的程序
    _program = glCreateProgram();
    glAttachShader(_program, vertexShader);
    glAttachShader(_program, fragmentShader);
    
    //連接程序
    glLinkProgram(_program);
    
    //檢查是否有錯誤, 有的話獲取錯誤信息
    if(![self validateProgram:_program]) {
        
        glDeleteProgram(_program);
        exit(1);
    }
    
    //告訴OpenGL使用該程序
    glUseProgram(_program);
    
    //這里是獲取剛才著色器里面的變量并使用
    _positionSlot = glGetAttribLocation(_program, "Position");
    _colorSlot    = glGetAttribLocation(_program, "InColor");
    glEnableVertexAttribArray(_positionSlot);
    glEnableVertexAttribArray(_colorSlot);
}

//驗證鏈接程序是否有效
- (BOOL)validateProgram:(GLuint)prog {
    
    GLint linkSuccess;
    glGetProgramiv(prog, GL_LINK_STATUS, &linkSuccess);
    if(linkSuccess == GL_FALSE) {
        GLchar messages[256];
        glGetProgramInfoLog(prog, sizeof(messages), 0, &messages[0]);
        NSString *messageString = [NSString stringWithUTF8String:messages];
        NSLog(@"%@", messageString);
        return NO;
    }
    
    return YES;
}

8.聲明矩形四個角對應的數據

//4個頂點(分別表示xyz軸)
static const float Vertices[] = {
    
    -0.5, -0.5, 0,  //左下
     0.5, -0.5, 0,  //右下
    -0.5,  0.5, 0,  //左上
     0.5,  0.5, 0,  //右上
};
//4個點的顏色(分別表示RGBA值)
static const float Colors[] = {
    1,0,0,1,
    0,1,0,1,
    0,0,1,1,
    0,0,0,1,
};

9.開始渲染

- (void)render:(CADisplayLink *)displayLink {
    
    //用指定的顏色清除,清除顏色被設置為(0.5f, 0.5f, 0.5f, 1.0f), 所以為黑色
    glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    
    //設定窗口的范圍(如果不是很明白, 可以自己動手修改下試試)
    //他這個是左下角為(0,0) 右上角為(width,height)
    glViewport(0, 0, _width, _height);
    
    //指定了渲染時索引值為 index 的頂點屬性數組的數據格式和位置。
    /*
     *  indx:指定要修改的頂點屬性的索引值
     *  size:指定每個頂點屬性的組件數量。(必須坐標xyz軸就是3, 顏色rgba就是4)
     *  type:指定數組中每個組件的數據類型。(一般為GL_FLOAT)
     *  normalized:一般為GL_FALSE
     *  stride:指定連續頂點屬性之間的偏移量。如果為0,那么頂點屬性會被理解為:它們是緊密排列在一起的。初始值為0。
     *  ptr:指向數據的指針
     */
    glVertexAttribPointer(_positionSlot, 3, GL_FLOAT, GL_FALSE, 0, Vertices);
    glVertexAttribPointer(_colorSlot,    4, GL_FLOAT, GL_FALSE, 0, Colors);
    
 
    glDrawArrays(GL_TRIANGLE_STRIP, 0, sizeof(Vertices) / (sizeof(int) * 3));
    
    //把緩沖區的數據呈現到UIView上
    [_context presentRenderbuffer:GL_RENDERBUFFER];
}

glDrawArrays的第一個屬性有如下圖三種繪制方式.
我們例子中用到的是GL_TRIANGLE_STRIP順序是(左下右下左上右上).
當然我們也可以用GL_TRIANGLE_FAN,但是順序需要修改為(左下右下右上左上)這種方式,
第一種GL_TRIANGLES表示沒玩過~(> _ <)~. 原文鏈接

10.釋放資源

- (void)dealloc {
    
    //刪除綁定的渲染緩沖區
    if(_renderBuffer) {
        glDeleteRenderbuffers(GL_RENDERBUFFER, &_renderBuffer);
    }
    
    //刪除綁定的幀緩沖區
    if(_frameBuffer) {
        glDeleteFramebuffers(GL_FRAMEBUFFER, &_frameBuffer);
    }
    
    //釋放著色器
    if(_vertexShader) {
        
        //刪除頂點著色器連接
        glDetachShader(_program, _vertexShader);
        
        //刪除頂點著色器
        glDeleteShader(_vertexShader);
    }
    
    if(_fragmentShader) {
        
        //刪除片段著色器連接
        glDetachShader(_program, _fragmentShader);
        
        //刪除片段著色器
        glDeleteShader(_fragmentShader);
    }
    
    if(_program) {
        glDeleteProgram(_program);
    }
}
  • 自定義OpenGLView代碼就是這么多,不過里面有很多代碼都是固定的.里面有很多固定的代碼我都寫在了一個父類里面了.在這里獻上Demo.

  • 我自己測試的時候發現這個Demo里面有資源沒釋放,但是找不到,求大神。
  • 推薦一本書<<OpenGL ES 3.0 編程指南>>這本書相當于字典一樣,所用到的函數都會講解

  • 推薦一個學習OpenGL ES的網站, 后面我也會跟大家一起做里面的例子.

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,702評論 6 534
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,615評論 3 419
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 176,606評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,044評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,826評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,227評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,307評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,447評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,992評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,807評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,001評論 1 370
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,550評論 5 361
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,243評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,667評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,930評論 1 287
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,709評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,996評論 2 374

推薦閱讀更多精彩內容