本節書摘來異步社群《opengl es 3.x遊戲開發(下卷)》一書中的第1章,第1.4節,作者: 吳亞峰 責編: 張濤,更多章節内容可以通路雲栖社群“異步社群”公衆号檢視。
前面幾節已經介紹了頂點緩沖區對象、頂點數組對象以及一緻緩沖區對象,通過使用這些技術可以在很大程度上提高繪制效率。本節将介紹在某些情況下可以進一步提高效率的映射緩沖區對象(mapping buffer objects)。
本章前面的幾個案例中,都是通過調用glbufferdata方法或者glbuffersubdata方法向緩沖區中送入資料或者更新資料的。采用這種政策時需要首先将資料在記憶體中準備好,然後再通過glbufferdata方法或glbuffersubdata方法将資料從記憶體複制到顯存中。
這對于緩沖中資料不變或變化率很低的情況基本夠用了,但是對于繪制過程中頻繁變化的資料就顯得效率不夠高。本小節将介紹一種針對此問題的解決方案——映射緩沖區對象。通過使用映射緩沖區對象,可以在繪制過程中資料頻繁變化的情況下進一步減少記憶體消耗并提高渲染效率。
提示 所謂映射緩沖區對象就是将顯存中的存儲映射到虛拟的記憶體位址上,使得開發人員可以使用如同通路記憶體一樣的api通路顯存以提高效率。
使用映射緩沖區對象主要涉及的方法有3個,具體内容如下。
glmapbufferrange方法
glmapbufferrange方法用于将指定緩沖對應的顯存映射到虛拟的記憶體位址上,并傳回映射的結果,以便開發人員使用它來更新顯存中的資料。如果出現錯誤或者發出無效請求,該方法将傳回空,其具體方法簽名如下。
說明 參數target用于描述需映射的緩沖區類型,可以設定的值如本章前面表1-1所列;參數offset為被映射的緩沖區資料存儲中的偏移量;參數length為需要映射的緩沖區資料位元組數;參數access為通路标志,可選的通路标志如表1-6所列。

表1-6中的通路标志在不沖突的情況下可以同時使用多個标志,使用多個标志時用“|”隔開。另外,實際開發中一般至少選用gl_map_read_bit與gl_map_write_bit中的一個,而其他選項則進一步根據需要選擇即可。
glunmapbuffer方法
glunmapbuffer方法用于解除緩沖區映射,其具體方法簽名如下。
1 public static boolean glunmapbuffer (int target)
參數target用于描述需解除映射的緩沖區類型,可以設定的值如本章前面表1-1所列。如果解除映射操作成功,則傳回true,并且前面glmapbufferrange方法傳回的映射範圍在取消映射操作成功之後不再可用。如果頂點緩沖區對象資料存儲中的資料在緩沖區映射之後已經破壞,則glunmapbuffer方法傳回false。
glflushmappedbufferrange方法
glflushmappedbufferrange方法用于通知渲染管線被映射緩沖區中的資料已經被修改,類似于i/o操作時用于重新整理資料的flush方法,其具體方法簽名如下。
參數target用于描述需重新整理資料所屬的被映射緩沖區類型;參數offset為被映射的緩沖區資料存儲中的偏移量;參數length為需要重新整理的被映射緩沖區資料位元組數。需要注意的是,glflushmappedbufferrange方法所操作的緩沖必須用glmapbufferrange方法在映射時選用了gl_map_flush_explicit_bit選項。
了解了映射緩沖區對象的基本知識以後,就可以進行案例的開發了。在開發案例之前,首先應該了解本節案例sample1_4的運作效果,具體情況如圖1-4所示。
從圖1-4中可以看出,運作過程中球體的上半部分在球體與立方體之間連續變換着。這是由于運作過程中程式不斷進行緩沖區映射,并連續更新球體上半部分的頂點資料。
了解了映射緩沖區對象的基本知識與案例效果後,就可以進行代碼的開發了。由于本案例中的很多類與前面案例中的很相似,是以這裡僅給出本案例中具有特殊性及代表性的代碼,具體内容如下。
(1)首先介紹的是ballandcube類中用于初始化頂點資料的initvertexdata方法,在該方法中向頂點坐标資料緩沖中送入資料時就采用了映射緩沖區,具體内容如下。
上述代碼中最有代表性的就是第8~第21行,其中沒有使用傳統的glbufferdata方法向頂點坐标資料緩沖中送入資料,而是使用glmapbufferrange方法先進行緩沖映射,然後再直接将資料送入映射後的緩沖中。需要注意的是,每次映射并更新資料完畢後,都需要使用glunmapbuffer方法解除映射,否則渲染管線在繪制時,無法正常使用被映射緩沖區中的資料。
(2)了解了用于初始化頂點資料的initvertexdata方法後,接下來要介紹的是用于在運作過程中連續計算頂點坐标的方法calvertices和插值方法insertvalue,具體代碼如下。
第1~第7行為在運作過程中不斷被調用以計算目前頂點坐标位置為從球到立方體或從立方體到球變化服務的calvertices方法。此方法每次被調用時周遊每個頂點坐标,根據count參數以及flag參數值調用insertvalue方法完成插值計算。需要特别注意的是第5~第7行,每次計算完成後,将資料複制進繪制用數組時都需要加鎖,以避免複制的同時進行繪制造成畫面撕裂的問題。
第8~第15行為在球與立方體之間進行頂點坐标插值計算的insertvalue方法,從代碼中可以看出此方法采用的是線性插值。
(3)接下來詳細介紹用于更新頂點資料到映射緩沖區的方法updatemapping,其功能為根據接收到的頂點坐标資料更新頂點緩沖區對象中的資料,具體代碼如下。
上述方法在運作中定時被調用,用于将計算出來的新的頂點坐标資料更新到對應的緩沖中,供渲染管線在繪制時使用,主要套路與前面initvertexdata方法中的對應部分相同。
(4)了解了在繪制過程中不斷被調用以便計算與更新頂點坐标的相關方法後,下面來了解一下定時執行ballandcube類中calvertices方法的線程類——updatethread,其具體代碼如下。
上述updatethread類非常簡單,主要是在其run方法中定時調用calvertices方法更新頂點坐标資料。同時每次更新後會檢查是否達到一輪變化所需的總步驟數,若達到了則将步驟計數器置0,并将變化方向标志位isballcube(用于表示變化方向是從球到立方體還是立方體到球)置返。
(5)上一步介紹了定時執行ballandcube類中calvertices方法的線程類——updatethread,接下來介紹ballandcube類中的繪制方法drawself,其具體代碼如下。
第2~第4行設定物體繞x、y、z軸旋轉指定的角度。
第5~第18行首先指定使用某套着色器程式,然後将最終變換矩陣送入渲染管線,接着綁定了頂點位置、紋理坐标緩沖,并指定頂點位置、紋理坐标使用對應的緩沖,同時還啟用了頂點坐标以及紋理坐标資料數組。
第19~第23行激活并綁定了指定的紋理,然後同步加鎖後調用updatemapping方法更新頂點資料。這裡的同步加鎖與前面calvertices方法中是對應的,目的是為了避免更新緩沖中資料的同時資料數組curballfordraw被其他線程通路。
第24~第28行首先綁定索引緩沖對象,并以三角形方式執行繪制,最後綁定到系統預設的索引與數組緩沖。