iOS開發(fā)學(xué)習(xí)OpenGL ES系列 -- VBO&VAO

不知道在這里講VBO&VAO是否合適硕并,我的想法是早一點了解這一塊明吩,后面的例子中我們都運用上以加深印象與熟練度。

首先我們知道OpenGL ES部分運行在CPU上哗魂,部分運行在GPU上勇婴,所以O(shè)penGL ES橫跨在兩個處理器之間忱嘹,協(xié)調(diào)兩個內(nèi)存區(qū)域之間的數(shù)據(jù)交換。

對于渲染速度耕渴,最快的數(shù)據(jù)交換方式是沒有數(shù)據(jù)交換拘悦。從一個內(nèi)存區(qū)域賦值數(shù)據(jù)到另一個內(nèi)存速度是相對較慢的。最致命的是橱脸,在內(nèi)存復(fù)制發(fā)生的時候GPU和CPU都不能把內(nèi)存另作它用础米。因此內(nèi)存區(qū)域之間的數(shù)據(jù)交換需要盡量避免。

其次添诉,所有的內(nèi)存訪問都是相對較慢的屁桑,現(xiàn)在的嵌入式CPU可以很容易完成大約每秒一億次的運算,但是它只能每秒讀寫內(nèi)存200萬次吻商。這意味著掏颊,除非CPU能夠在每次從內(nèi)存讀取一塊數(shù)據(jù)后有效地運行五個或者更多個運算,否則處理器的性能就處于次優(yōu)狀態(tài)艾帐,這種狀態(tài)叫做"數(shù)據(jù)饑餓"乌叶。這種情況對于CPU來說更明顯,在理想條件下柒爸,GPU能夠每秒執(zhí)行數(shù)億次運算准浴,但是卻只能每秒訪問內(nèi)存2億次。GPU幾乎總是受限于內(nèi)存訪問的性能捎稚,并且通常需要在每塊數(shù)據(jù)上執(zhí)行10~30次運算才不會影響整體的圖形輸出乐横。(摘自O(shè)penGL ES應(yīng)用開發(fā)實踐)

上面這段話的大概意思是:GPU的性能并不在于數(shù)據(jù)處理求橄,而是在于數(shù)據(jù)讀取上。就前一篇的例子而言葡公,如果要保持矩形一直存在于屏幕上罐农,CPU就需要不斷的發(fā)送頂點數(shù)據(jù)到GPU上,否則的話就會被設(shè)置的清除色覆蓋催什。這就對應(yīng)于激活頂點屬性涵亏,然后發(fā)送數(shù)據(jù)到屬性上,在進(jìn)行繪制的那段代碼:

static GLfloat vertices[] = {
        // 第一個三角形
     0.5f,   0.5f,  0.0, 1.0 , 0.0, 0.0,
     0.5f,  -0.5f,  0.0, 1.0 , 0.0, 0.0,
    -0.5f,  -0.5f,  0.0, 1.0 , 0.0, 0.0,
        
        // 第二個三角形
     -0.5,  -0.5f,  0.0, 1.0 , 0.0, 0.0,
     -0.5f,  0.5f,  0.0, 1.0 , 0.0, 0.0,
      0.5f,  0.5f,  0.0, 1.0 , 0.0, 0.0,
};

// 啟用這個著色器程序
glUseProgram(program);
    
// 啟用著色器屬性和獲取位置
GLuint positionAttribLocation = glGetAttribLocation(program, "position");
glEnableVertexAttribArray(positionAttribLocation);
    
GLuint colorAttribLocation = glGetAttribLocation(program, "color");
glEnableVertexAttribArray(colorAttribLocation);
    
glVertexAttribPointer(positionAttribLocation, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (char *)vertices);
glVertexAttribPointer(colorAttribLocation, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (char *)vertices + 3 * sizeof(GLfloat));
    
// 繪制
glDrawArrays(GL_TRIANGLES, 0, 6);

在glkView:(GLKView *)view drawInRect:(CGRect)rect方法中會一直持續(xù)這個過程蒲凶,從CPU傳遞vertices到GPU內(nèi)存气筋,也就是每次渲染都會傳遞一次。雖然這里的繪制數(shù)據(jù)量比較小旋圆,但是到后面繪制復(fù)雜的圖形事宠默,可能會導(dǎo)致數(shù)據(jù)傳遞延遲,這就嚴(yán)重影響了繪制幀率灵巧。

VBO
VBO全稱Vertex Buffer Object -- 頂點緩存對象搀矫。OpenGL ES對于數(shù)據(jù)傳遞效率低下問題提供的辦法是提前傳遞數(shù)據(jù)到GPU內(nèi)存緩存區(qū),繪制的時候GPU就可以非澈⒌龋快速地讀取自己內(nèi)存區(qū)域的數(shù)據(jù)艾君,這樣就可以減少數(shù)據(jù)傳遞次數(shù),避免數(shù)據(jù)傳遞延遲造成的繪制幀率降低問題肄方。而VBO就可以使用頂點緩存能夠大大較少了CPU到GPU 之間的數(shù)據(jù)拷貝開銷,因此顯著地提升了程序運行的效率蹬癌。下面是創(chuàng)建代碼:

// 使用glGenBuffers函數(shù)和一個緩沖ID生成一個VBO對象
glGenBuffers(1, &vertexID);
    
/*
使用glBindBuffer函數(shù)把新創(chuàng)建的緩沖綁定到GL_ARRAY_BUFFER目標(biāo)上
參數(shù): 
target :指定綁定的目標(biāo)权她,取值為 GL_ARRAY_BUFFER(用于頂點數(shù)據(jù)) 或 GL_ELEMENT_ARRAY_BUFFER(用于索引數(shù)據(jù))
buffer :頂點緩存對象句柄
*/ 
glBindBuffer(GL_ARRAY_BUFFER, vertexID);
    
/*
調(diào)用glBufferData函數(shù),它會把之前定義的頂點數(shù)據(jù)復(fù)制到緩沖的內(nèi)存中
參數(shù):
target:與 glBindBuffer 中的參數(shù) target 相同逝薪;
size :指定頂點緩存區(qū)的大小隅要,以字節(jié)為單位計數(shù);
data :用于初始化頂點緩存區(qū)的數(shù)據(jù)
usage:表示該緩存區(qū)域?qū)蝗绾问褂枚茫闹饕康氖怯糜趯υ摼彺鎱^(qū)域做何種程度的優(yōu)化
      GL_STATIC_DRAW:表示該緩存區(qū)不會被修改
      GL_DYNAMIC_DRAW:表示該緩存區(qū)會被周期性更改
      GL_STREAM_DRAW:表示該緩存區(qū)會被頻繁更改
*/ 
glBufferData(GL_ARRAY_BUFFER, 36 * sizeof(GLfloat), vertices, GL_STATIC_DRAW);

數(shù)據(jù)提前緩存到GPU內(nèi)存之后步清,glkView:(GLKView *)view drawInRect:(CGRect)rect方法中就需要進(jìn)行修改了,不能再每次繪制都向GPU發(fā)送頂點數(shù)據(jù)虏肾。因為我們的數(shù)據(jù)已經(jīng)發(fā)送到GPU內(nèi)存中廓啊,現(xiàn)在就是需要告訴GPU如果去識別那些數(shù)據(jù)以及去使用了。

VAO
VAO被稱為頂點數(shù)組對象封豪,它并不存儲數(shù)據(jù)谴轮。它可以像頂點緩沖對象那樣被綁定,任何隨后的頂點屬性調(diào)用都會儲存在這個VAO中吹埠。這樣的好處就是第步,當(dāng)配置頂點屬性指針時疮装,你只需要將那些調(diào)用執(zhí)行一次,之后再繪制物體的時候只需要綁定相應(yīng)的VAO就行了粘都。這使在不同頂點數(shù)據(jù)和屬性配置之間切換變得非常簡單廓推,只需要綁定不同的VAO就行了。剛剛設(shè)置的所有狀態(tài)都將存儲在VAO中翩隧。

一個頂點數(shù)組對象會儲存以下這些內(nèi)容:

  • glEnableVertexAttribArray和glDisableVertexAttribArray的調(diào)用樊展。
  • 通過glVertexAttribPointer設(shè)置的頂點屬性配置。
  • 通過glVertexAttribPointer調(diào)用與頂點屬性關(guān)聯(lián)的頂點緩沖對象鸽心。

下面通過代碼創(chuàng)建一個VAO:

glGenVertexArraysOES(1, &vao);
    
// 綁定vao
glBindVertexArrayOES(vao);
    
// 把頂點數(shù)組復(fù)制到緩沖中供OpenGL ES使用
glBindBuffer(GL_ARRAY_BUFFER, vertexID);

GLuint positionAttribLocation = glGetAttribLocation(program, "position");
glEnableVertexAttribArray(positionAttribLocation);
    
GLuint colorAttribLocation = glGetAttribLocation(program, "color");
glEnableVertexAttribArray(colorAttribLocation);
    
glVertexAttribPointer(positionAttribLocation, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), NULL);
glVertexAttribPointer(colorAttribLocation, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), NULL + 3 * sizeof(GLfloat));

glBindVertexArrayOES(0);

接下來的繪制就簡單了滚局,通過glBindVertexArrayOES啟用VAO就可以完成繪制。

glClear(GL_COLOR_BUFFER_BIT);
glClearColor(1.0, 1.0, 1.0, 1.0);

// 啟用這個著色器程序
glUseProgram(program);

glBindVertexArrayOES(vao);

// 繪制
glDrawArrays(GL_TRIANGLES, 0,6);

現(xiàn)在glkView:(GLKView *)view drawInRect:(CGRect)rect中就這么簡單了顽频。

這里有一點需要說明藤肢,我們現(xiàn)在是在OpenGL ES 2.0上使用VAO,這里就得使用蘋果擴(kuò)展的相關(guān)API糯景,既帶有'OES'的函數(shù):

GLvoid glGenVertexArraysOES(GLsizei n, GLuint *arrays)
GLvoid glBindVertexArrayOES(GLuint array)
GLvoid glDeleteVertexArraysOES(GLsizei n, const GLuint *arrays)
GLboolean glIsVertexArrayOES(GLuint array);

如果我們使用的是ES3嘁圈,那么就就是對應(yīng)的這些函數(shù),照樣可以完成繪制:

GLvoid glGenVertexArrays(GLsizei n, GLuint *arrays)
GLvoid glBindVertexArray(GLuint array)
GLvoid glDeleteVertexArrays(GLsizei n, const GLuint *arrays)
GLboolean glIsVertexArray(GLuint array);

這樣的話蟀淮,頂點數(shù)據(jù)經(jīng)過VBO和VAO的優(yōu)化最住,就不需要每次繪制前進(jìn)行數(shù)據(jù)傳遞和屬性綁定了,大大提高了繪制速度怠惶。

最后的效果圖:

附上代碼:LearningOpenGL ES GitHub

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末涨缚,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子策治,更是在濱河造成了極大的恐慌脓魏,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件通惫,死亡現(xiàn)場離奇詭異茂翔,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)履腋,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進(jìn)店門珊燎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人遵湖,你說我怎么就攤上這事悔政。” “怎么了奄侠?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵卓箫,是天一觀的道長。 經(jīng)常有香客問我垄潮,道長烹卒,這世上最難降的妖魔是什么闷盔? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮旅急,結(jié)果婚禮上逢勾,老公的妹妹穿的比我還像新娘。我一直安慰自己藐吮,他們只是感情好溺拱,可當(dāng)我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著谣辞,像睡著了一般迫摔。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上泥从,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天句占,我揣著相機(jī)與錄音,去河邊找鬼躯嫉。 笑死纱烘,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的祈餐。 我是一名探鬼主播擂啥,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼帆阳!你這毒婦竟也來了哺壶?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤蜒谤,失蹤者是張志新(化名)和其女友劉穎变骡,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體芭逝,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年渊胸,在試婚紗的時候發(fā)現(xiàn)自己被綠了旬盯。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡翎猛,死狀恐怖胖翰,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情切厘,我是刑警寧澤萨咳,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站疫稿,受9級特大地震影響培他,放射性物質(zhì)發(fā)生泄漏鹃两。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一舀凛、第九天 我趴在偏房一處隱蔽的房頂上張望俊扳。 院中可真熱鬧,春花似錦猛遍、人聲如沸馋记。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽梯醒。三九已至,卻和暖如春腌紧,著一層夾襖步出監(jiān)牢的瞬間茸习,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工寄啼, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留逮光,地道東北人。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓墩划,卻偏偏與公主長得像涕刚,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子乙帮,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,916評論 2 344