OpenGL ES學(xué)習(xí)筆記之一(創(chuàng)建一個三角形)

我是搞iOS開發(fā)的缸逃,所以本博客會用到OC的代碼作例子针饥。OpenGL ES的接口是用C寫的,對于一些矩陣的操作還需要第三方式的庫需频,看到這里是不是整個人都不好了的感覺丁眼!我開始學(xué)的時候也是這種感覺。但有一個好消息要告訴你蘋果對OpenGL ES進(jìn)行的部分封裝出了一個GLKit昭殉,你在對矩陣操作的時候不再需要借助第三方也可以實現(xiàn)苞七。下面的講解會使用兩方式,一種是使用GLKit一種是不使用GLKit挪丢。

我也是在學(xué)習(xí)階段蹂风,可能里面會有錯誤如有錯誤還請各位大神指出。本博客不能算是基礎(chǔ)教程里面介紹會少一點乾蓬,如果你一點都不了解OpenGL ES可以先看這個博客然后回來再看我這里的惠啄。

學(xué)習(xí)的代碼都在我的github倉庫歡迎大家學(xué)習(xí)指教!

不使用GLKit的方法

代碼的步驟如下:

  • 創(chuàng)建和設(shè)置CAEAGLLayer
  • 創(chuàng)建和設(shè)置EAGLContext
  • 申請和綁定渲染緩存
  • 申請和綁定幀緩存
  • 編譯鏈接著色器
  • 創(chuàng)建和設(shè)置頂點信息
  • 渲染顯示頂點圖形
創(chuàng)建項目

首先創(chuàng)建一個空項目任内,然后導(dǎo)入兩個庫

導(dǎo)入庫.png

在Xcode中創(chuàng)建一個空項目撵渡,因為需要用到著色器我們我們要創(chuàng)建2個空文件一個命名為vertexShader.glsl(頂點著色器)一個命名為fragmentShader.glsl(片元著色器)。最后項目如下樣子:

創(chuàng)建后項目的樣子.png

在著色器文件里要用著色器語言來寫代碼死嗦,這種語言叫OpenGL Shading Language簡稱glsl趋距,其語法有點像C,如果你會C就很容易明白代碼的含意不過我會盡量在代碼里添加注釋越走。我們在vertexShader.glsl文件里寫入好下代碼:

attribute vec4 myPosition; // 聲明一個變量

void main()
{
    gl_Position = myPosition; // 設(shè)置頂點位置信息
}

在fragmentShader.glsl語言里添加如下代碼:

precision mediump float; // 聲明精度棚品,在頂點著色器里可以省略精度但是片元著色器里不能省略

void main()
{
    gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); // 為了簡單這里將顏色設(shè)成固定值
}

在控制器里導(dǎo)入OpenGL ES的頭文件:

#import <OpenGLES/ES2/gl.h>
#import <OpenGLES/ES2/glext.h>

然后聲明幾個變量

    CAEAGLLayer * _myLayer; // 用于顯示的layer
    EAGLContext * _myContext; // 用于管理狀態(tài)的context
    
    GLuint _myFrameBuffer; // 幀緩存
    GLuint _myRenderBuffer; // 渲染緩存
    
    GLuint _myPrograme; // 用于鏈接著色器的程序
    GLuint _myPositionSlot; // 用于向著色器傳遞頂點數(shù)據(jù)的槽

變量的用途上面的注釋已經(jīng)寫清楚了。下面就是完整的代碼了:

- (void)viewDidLoad {
    [super viewDidLoad];
    
    /*** 創(chuàng)建顯示的layer ***/
    _myLayer = [[CAEAGLLayer alloc] init];
    _myLayer.frame = self.view.frame;
    _myLayer.opaque = YES; // 設(shè)置為不透明
    // 設(shè)置kEAGLDrawablePropertyRetainedBacking為NO表示不維持上一次繪制的內(nèi)容
    // kEAGLColorFormatRGBA8表示設(shè)置色彩空間為RGBA8
    _myLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:@(NO), kEAGLDrawablePropertyRetainedBacking,kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat,  nil];
    // 將創(chuàng)建的layer添加到視圖
    [self.view.layer addSublayer:_myLayer];
    
    /*** 創(chuàng)建和設(shè)置context ***/
    
    // 創(chuàng)建的context為OpenGL ES 2.0廊敌,因為2.0開始支持可編輯管線且大多數(shù)蘋果設(shè)備都支持
    _myContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
    
    if (nil == _myContext) {
        NSLog(@"context 創(chuàng)建失敗");
    }
    // 將創(chuàng)建的context設(shè)置為當(dāng)前context
    if (![EAGLContext setCurrentContext:_myContext]) {
        NSLog(@"context 設(shè)置失敗");
    }
    
    /*** 申請和綁定渲染緩存和幀緩存 ***/
    // 為renderbuffer申請一個id
    glGenRenderbuffers(1, &_myRenderBuffer);
    // 設(shè)置當(dāng)前的renderbuffer為剛申請的
    glBindRenderbuffer(GL_RENDERBUFFER, _myRenderBuffer);
    // 為renderbuffer分配存儲空間
    [_myContext renderbufferStorage:GL_RENDERBUFFER fromDrawable:_myLayer];
    // 為framebuffer申請一個id
    glGenFramebuffers(1, &_myFrameBuffer);
    // 設(shè)置當(dāng)前的framebuffer為剛申請的
    glBindFramebuffer(GL_FRAMEBUFFER, _myFrameBuffer);
    // 將renderbuffer關(guān)聯(lián)到framebuffer上
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, _myRenderBuffer);
    
    /*** 編譯鏈接著色器 ***/
    [self setupPrograme];
    
    /*** 設(shè)置頂點信息 ***/
    // 創(chuàng)建三角形的頂點
    GLfloat vertexes[] = {
        
        -0.5f, -0.5f, 0.0f,
        0.5f, -0.5f, 0.0f,
        0.0f, 0.5f, 0.0f
    };
    // 設(shè)置頂點數(shù)據(jù)的指針信息
    glVertexAttribPointer(_myPositionSlot, // 傳入對應(yīng)數(shù)據(jù)槽的位置
                          3, // 一組有多少數(shù)據(jù),即3個一組
                          GL_FLOAT, // 數(shù)據(jù)類型
                          GL_FALSE, // 是否是正交視圖
                          sizeof(GLfloat) * 3, // 數(shù)據(jù)跨度
                          vertexes // 頂點數(shù)據(jù)
                          );
    // 啟用頂點數(shù)據(jù)
    glEnableVertexAttribArray(_myPositionSlot);
    
    /*** 渲染顯示三角形 ***/
    // 設(shè)置顯示區(qū)域
    glViewport(0, 0, self.view.frame.size.width, self.view.frame.size.height);
    // 設(shè)置清屏顏色
    glClearColor(1.0, 1.0, 1.0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT);
    // 繪制三角開
    glDrawArrays(GL_TRIANGLES, 0, 3);
    // 顯示三角形
    [_myContext presentRenderbuffer:GL_RENDERBUFFER];
}


/**
 編譯和鏈接程序
 */
- (void)setupPrograme{
    
    GLuint vertexShader = [self loadShader:GL_VERTEX_SHADER withFileName:@"vertexShader.glsl"];
    GLuint fragmentShader = [self loadShader:GL_FRAGMENT_SHADER withFileName:@"fragmentShader.glsl"];
    // 創(chuàng)建一個程序
    _myPrograme = glCreateProgram();
    
    if (!_myPrograme) {
        NSLog(@"創(chuàng)建programe失敗");
    }
    // 添加著色器到程序中并鏈接
    glAttachShader(_myPrograme, vertexShader);
    glAttachShader(_myPrograme, fragmentShader);
    glLinkProgram(_myPrograme);
    
    GLint success = 0;
    // 獲取程序信息
    glGetProgramiv(_myPrograme, GL_LINK_STATUS, &success);
    if (success == GL_FALSE) { // 程序鏈接失敗
        
        GLint infoLen = 0;
        // 獲取錯誤信息長度
        glGetProgramiv(_myPrograme, GL_INFO_LOG_LENGTH, &infoLen);
        if (infoLen > 0) {
            // 申請內(nèi)存存放錯誤信息
            GLchar * info = malloc(sizeof(GLchar) * infoLen);
            glGetProgramInfoLog(_myPrograme, sizeof(GLchar) * infoLen, &infoLen, info);
            
            NSLog(@"%s", info);
        }
        // 刪除鏈接失敗的程序
        glDeleteProgram(_myPrograme);
        _myPrograme = 0;
        return;
    }
    // 啟用程序
    glUseProgram(_myPrograme);
    // 獲取頂點著色器myPosition的內(nèi)存地址
    _myPositionSlot = glGetAttribLocation(_myPrograme, "myPosition");
}


/**
 創(chuàng)建和編譯著色器
 
 @param type 著色器類型
 @param fileName 著色器文件
 @return 返回創(chuàng)建好的著色器门怪,創(chuàng)建失敗則返回0
 */
- (GLuint)loadShader:(GLenum)type withFileName:(NSString *)fileName{
    // 獲取文件路徑
    NSString * path = [[NSBundle mainBundle] pathForResource:fileName ofType:nil];
    
    NSError * error;
    NSString * shaderString = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:&error];
    // 轉(zhuǎn)成C字符串
    const GLchar * shaderStringUTF8 = shaderString.UTF8String;
    
    if (error) {
        NSLog(@"文件讀取錯誤:%@",error);
    }
    // 創(chuàng)建著色器程序
    GLuint shader = glCreateShader(type);
    // 給著色器程序傳遞著色器字符串
    glShaderSource(shader, 1, &shaderStringUTF8, NULL);
    // 編譯
    glCompileShader(shader);
    // 查看編譯情況
    GLint compiled = 0;
    // 獲取著色器信息
    glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
    if (compiled == GL_FALSE) {
        
        GLint infoLen = 0;
        // 獲取錯誤信息的長度
        glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
        
        if (infoLen > 0) {
            
            GLchar * info = malloc(sizeof(GLchar) * infoLen);
            
            glGetShaderInfoLog(shader, // 對應(yīng)的著色器
                               sizeof(GLchar) * infoLen, // buffer的大小
                               &infoLen, // 傳入錯誤長度
                               info); // 存放錯誤信息的內(nèi)存
            
            NSLog(@"著色器錯誤:%s", info);
            free(info); // 釋放內(nèi)存
        }
        // 移除創(chuàng)建失敗的著色器
        glDeleteShader(shader);
        return 0;
    }
    return shader;
}

顯示渲染結(jié)果:

渲染結(jié)果.png

上面是控制器里主要的代碼骡澈,注釋已經(jīng)加的很詳細(xì)的就不再解釋了。下面看用使用GLKit后代碼的樣子掷空。

使用GLKit的方法

首先創(chuàng)建一個空項目和上面一樣先導(dǎo)入2個庫肋殴,然后讓ViewController繼承自GLKViewController囤锉。再到Main.storyboard里添加一個GLKViewController控制器并設(shè)成初始化控制器。別忘了與ViewController綁定护锤。

接下來是代碼時間官地,先在ViewController里聲明一個屬性:

@property (nonatomic,strong)GLKBaseEffect * effect;

然后我們在全局區(qū)聲明一個頂點數(shù)組:

GLfloat vertexes[] = {
    
    -0.5f, -0.5f, 0.0f,
    0.5f, -0.5f, 0.0f,
    0.0f, 0.5f, 0.0f
};

接下來就是全部主要代碼了:

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 獲取控制器的view
    GLKView * glView = (GLKView *)self.view;
    // 設(shè)置當(dāng)前的context
    EAGLContext * context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
    if (nil == context) {
        NSLog(@"context create failed");
    }

    glView.context = context;
    
    if (![EAGLContext setCurrentContext:context]) {
        NSLog(@"set currentContext failed");
    }
    // 創(chuàng)建GLKBaseEffect
    self.effect = [[GLKBaseEffect alloc] init];
    // 設(shè)置使用的顏色
    self.effect.useConstantColor = GL_TRUE;
    self.effect.constantColor = GLKVector4Make(1.0f, 0.0f, 0.0f, 1.0f);
    // 設(shè)置清屏顏色
    glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
    
    // 開啟頂點
    glEnableVertexAttribArray(GLKVertexAttribPosition);
    // 設(shè)置數(shù)據(jù)指針
    glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 3, vertexes);
}

- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect{
    
    glClear(GL_COLOR_BUFFER_BIT);
    
    [self.effect prepareToDraw];
    glDrawArrays(GL_TRIANGLES, 0, 3);
}

顯示結(jié)果如下:

顯示結(jié)果.png

對比一下是不是用了GLKit的代碼很簡單!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末烙懦,一起剝皮案震驚了整個濱河市驱入,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌氯析,老刑警劉巖亏较,帶你破解...
    沈念sama閱讀 216,651評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異掩缓,居然都是意外死亡雪情,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評論 3 392
  • 文/潘曉璐 我一進(jìn)店門你辣,熙熙樓的掌柜王于貴愁眉苦臉地迎上來巡通,“玉大人,你說我怎么就攤上這事舍哄⊙缌梗” “怎么了?”我有些...
    開封第一講書人閱讀 162,931評論 0 353
  • 文/不壞的土叔 我叫張陵蠢熄,是天一觀的道長跪解。 經(jīng)常有香客問我,道長签孔,這世上最難降的妖魔是什么叉讥? 我笑而不...
    開封第一講書人閱讀 58,218評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮饥追,結(jié)果婚禮上图仓,老公的妹妹穿的比我還像新娘。我一直安慰自己但绕,他們只是感情好救崔,可當(dāng)我...
    茶點故事閱讀 67,234評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著捏顺,像睡著了一般六孵。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上幅骄,一...
    開封第一講書人閱讀 51,198評論 1 299
  • 那天劫窒,我揣著相機與錄音,去河邊找鬼拆座。 笑死主巍,一個胖子當(dāng)著我的面吹牛冠息,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播孕索,決...
    沈念sama閱讀 40,084評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼逛艰,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了搞旭?” 一聲冷哼從身側(cè)響起散怖,我...
    開封第一講書人閱讀 38,926評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎选脊,沒想到半個月后杭抠,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,341評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡恳啥,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,563評論 2 333
  • 正文 我和宋清朗相戀三年偏灿,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片钝的。...
    茶點故事閱讀 39,731評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡翁垂,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出硝桩,到底是詐尸還是另有隱情沿猜,我是刑警寧澤,帶...
    沈念sama閱讀 35,430評論 5 343
  • 正文 年R本政府宣布碗脊,位于F島的核電站啼肩,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏衙伶。R本人自食惡果不足惜祈坠,卻給世界環(huán)境...
    茶點故事閱讀 41,036評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望矢劲。 院中可真熱鬧赦拘,春花似錦、人聲如沸芬沉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,676評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽丸逸。三九已至蹋艺,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間黄刚,已是汗流浹背车海。 一陣腳步聲響...
    開封第一講書人閱讀 32,829評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留隘击,地道東北人侍芝。 一個月前我還...
    沈念sama閱讀 47,743評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像埋同,于是被迫代替她去往敵國和親州叠。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,629評論 2 354

推薦閱讀更多精彩內(nèi)容