Opengl ES系列學習--序

     之前一直都想好好學習一下Opengl ES,因爲自己想往圖形界面處理方向集中發展,這樣的目標對Opengl ES的要求也是不能少的,推薦兩本書:《OpenGL ES應用開發實踐指南 Android卷》《OPENGL ES 3.0編程指南中文原書第2版》,CSDN上也有其他朋友分享的,但是都要四五十個積分,有點摳門了,另外自己把兩本書的代碼全部整理了下,Github下載鏈接:Opengl ES Source Code,所有項目使用AndroidStudio運行,全部都可直接運行,收集了Opengl ES2.0和Opengl ES3.0的代碼。

     如果大家是零基礎開始學習的話,作爲一個踩坑過來的人,強烈推薦先看《OpenGL ES應用開發實踐指南  Android卷》,如果先看3.0的話,根本沒有一點眉目,最重點的vertex_shader和fragment_shader根本無法理解,把書看了三四遍,仍然一頭霧水。另外,看書的同時,不能光看,必須要自己照着寫,新建一個空白項目,一行一行手寫,這樣才能慢慢理解其中的原理。

     我們學習Opengl ES的重點內容有幾個:

     一、通過不斷的研究裏邊的源碼、修改、測試,要重點掌握頂點着色器和片斷着色器的原理。頂點着色器和片斷着色器應該說是Opengl的核心,如果能熟練掌握這兩個着色器,那麼你可以使用它們創建出非常炫的效果。對Application而言,這兩個着色器就是通過framework提供的API來進行控制的,它們都是直接運行在GPU上的一段代碼。

     二、通過Android提供的Opengl ES的API接口,如何控制頂點着色器和片斷着色器中的變量,從而實現炫麗的界面功能。

     三、學習過程中,還必須耐心的理解Matrix類中常用的方法,比如setIdentityM創建單位矩陣、translateM創建平移矩陣、multiplyMM將兩個矩陣相乘等等,全部都涉及到數學運算,確實非常頭疼,如果不關心數學運算的話,我們就簡單知道它能實現什麼樣的功能就可以了。

     當然,實現功能的同時,出錯肯定是少不了的,所以我們還得學會調試,強烈推薦使用Graphics API Debugger分析工具,單獨針對界面渲染出的,功能非常強大,安裝包下載地址:Graphics API Debugger,使用方法非常簡單,一定注意,使用的時候,要關閉AndroidStudio,否則無法連接設備。

     下面的內容就整理一下自己看過之後得到的一些經驗,方便大家查閱,網上相關的資料搜了下,好像很少,對初學者來說效率非常低,希望自己的整理能給大家節省一些時間。

1:Opengl ES 2.0和3.0的對比

相同點:

相比於 OpenGL ES 1.x 系列的固定功能管線,OpenGL ES 2.0和OpenGL ES 3.0都是可編程圖形管線。開發者可以自己編寫圖形管線中的頂點着色器和片段着色器兩個階段的代碼。

不同點:

1)兼容性
OpenGL ES 3.0是向後兼容 OpenGL ES 2.0 的。也就是說使用 2.0 編寫的應用程序是可以在 3.0 中繼續使用的。

2)新特性
採用陰影貼圖、體渲染(volume rendering)、基於 GPU 的粒子動畫、幾何形狀實例化、紋理壓縮和伽馬校正等技術的重要功能在 2.0 中都不具備。3.0 將這些功能引入,同時繼續適應嵌入系統的侷限性。

3)渲染管線
3.0 中移除了 Alpha 測試 和 邏輯操作(LogicOp) 兩部分,這兩部分存在於 OpenGL ES 2.0 和 OpenGL ES 1.x 中。
移除 Alpha 測試 是因爲片段着色器可能拋棄片段,因此 Alpha 測試可以在片段着色器中進行。移除 邏輯操作(LogicOp) 是因爲它很少被使用。

4)着色器腳本編寫
着色器腳本的編寫發生了比較大的變化,其中幾點爲:

  • 版本聲明

必須在着色器腳本中使用 #version 300 es 聲明爲指定使用 OpenGL ES 3.0 版本。例如:

#version 300 es

layout(location = 0) in vec4 vPosition;

void main() {
    gl_Position = vPosition;
}

而不添加版本聲明或者使用 #version 100 es 聲明版本則指定使用 OpenGL ES 2.0。以往 2.0 剛剛出來可編程的圖形管線,所以版本聲明爲 #version 100 es ,後來爲了使版本號相匹配,OpenGL ES 3.0 的 shader 版本直接從1.0 跳到了 3.0 。

  • 輸入輸出

3.0 中新增了 in,out,inout 關鍵字,用來取代 attribute 和 varying 關鍵字。同時 gl_FragColor 和 gl_FragData 也刪除了,片段着色器可以使用 out 聲明字段輸出。

  • 變量賦值

3.0 中可以直接使用 layout 對指定位置的變量賦值。例如:

# shader腳本中
layout (location = 1) uniform float a;

代碼中,直接寫上對應的 layout 的值就可以賦值

GLES30.glUniform1f(1, 1f);

而 2.0 中必須使用如下形式賦值:

GLES20.glUniform1f(GLES20.glGetAttribLocation(program, "a"), 1f)

新功能:
OpenGL ES 3.0 加入了許多新的功能和特性,使得我們的開發更加靈活和便捷,下面是 3.0 的一些新功能。

1、紋理

  • sRGB紋理和幀緩衝區——允許應用程序執行伽馬校正渲染。紋理可以保存在經過伽馬校正的sRGB空間,在着色器中讀取時反校正到線性空間,然後在輸出到幀緩衝區時轉換回sRGB伽馬校正空間。通過在線性空間中正確地進行照明和其它計算,可能得到更高的視覺保真度。
  • 2D紋理數組——保存一組2D紋理的紋理目標。例如這些數組可以用於執行紋理動畫。在2D紋理數組出現之前,這種動畫一般通過在單個2D紋理中平鋪動畫幀並修改紋理座標改變動畫幀來實現。有了2D紋理數組,動畫的每個幀可以在數組的一個2D切片中指定。
  • 3D紋理。一些OpenGL ES 2.0實現通過擴展支持3D紋理,而OpenGL ES3.0將此作爲強制的功能。
  • 深度紋理和陰影比較——啓用存儲在紋理中的深度緩衝區。深度紋理的最常見用途是渲染陰影,這時深度緩衝區從光源的角度渲染,然後用於在渲染場景時比較,以確定片段是否在陰影中。除了深度紋理外,OpenGL ES 3.0可以在讀取時比較深度紋理,從而在深度紋理上完成雙線性過濾。
  • 無縫立方圖。以往立方圖渲染可能在立方圖各面之間的邊界產生僞像。在OpenGL ES 3.0中,立方圖可以進行採樣如過濾來使用相鄰面的數據並刪除接縫處的僞像。
  • 浮點紋理。OpenGL ES 3.0擴展了支持的紋理格式。支持並可以過濾半浮點紋理(16位),也支持全浮點紋理(32位),但不能過濾。
  • ETC2/EAC紋理壓縮。OpenGL ES 3.0中強制支持ETC2/EAC。ETC2/EAC的格式爲RGB888,RGBA8888和單通道及雙通道有符號/無符號紋理數據。紋理壓縮的好處包括更好的性能以及減少GPU內存佔用。
  • 整數紋理。OpenGL ES 3.0引入了渲染和讀取保存爲未規範化有符號或無符號8位、16位和32位整數紋理的能力。
  • 其它紋理格式。OpenGL ES 3.0還包含了對11-11-10 RGB浮點紋理、共享指數RGB 9-9-9-5紋理、10-10-10-2整數紋理以及8位分量有符號規範化紋理的支持。
  • 非2冪次紋理(NPOT)。紋理現在可以指定爲不爲2的冪次尺寸。
  • 紋理細節級別(LOD)。現在可以強制使用用於確定讀取哪個Mipmap的LOD參數。此外,可以強制基本和最大Mipmap級別。這兩個功能組合起來,可以流化Mipmap。在更大的Mipmap級別可用時,可以提高基本級別,LOD值可以平滑地增加,以提供平滑的流化紋理。這一功能非常有用,例如用於通過網絡連接下載紋理Mipmap。
  • 紋理調配。引入新的紋理對象狀態,允許獨立控制紋理數據每個通道(R、G、B、A)在着色器中的映射。
  • 不可變紋理。爲應用程序提供在加載數據之前指定紋理格式和大小的機制。在這樣做的時候,紋理格式不可變,OpenGL ES驅動程序可以預先執行所有一致性和內存檢查。通過允許驅動程序在繪製的時候跳過一致性檢查,可以改善性能。
  • 最小尺寸增大。OpenGL ES 3.0支持遠大於OpenGL ES 2.0的紋理資源。

2、着色器

  • 二進制程序文件。在OpenGL ES 2.0中可以二進制格式存儲着色器,但是仍需要在運行時鏈接到程序。在OpenGL ES 3.0中,完全鏈接過的二進制程序文件可以保存爲離線二進制格式,運行時不需要鏈接步驟。這有助於減少應用程序的加載時間。
  • 強制的在線編譯器。在OpenGL ES 2.0可以選擇驅動程序是否支持着色器的在線編譯,意圖是降低驅動程序的內存需求,但是這一功能代價很大,開發人員不得不依靠供應商專用工具來生成着色器。在OpenGL ES 3.0中,所有實現都有在線着色器編譯器。
  • 非方矩陣。支持方陣之外的新矩陣類型,並在API中增加了相關的統一調用,以支持這些矩陣的加載。非方矩陣可以減少執行變換所需的指令。例如執行仿射變換時,可以使用43矩陣代替最後一行爲(0, 0, 0, 1)的44矩陣,從而減少執行變換所需的指令。
  • 全整數支持。支持整數(以及無符號整數)標量和向量類型以及全整數操作。有各種內建函數可以實現從整數到浮點數、從浮點數到整數的轉換以及從紋理中讀取整數值和向整數顏色緩衝區中輸出整數值的功能。
  • 質心採樣。爲了避免在多重採樣時產生僞像,可以用質心採樣聲明頂點着色器和片段着色器的輸出變量
  • 平面/平滑插值程序。在OpenGL ES 2.0中所有插值程序均隱含地在圖元之間採用線性插值。在OpenGL ES 3.0中插值程序可以顯式聲明爲平面或者平滑着色。
  • 統一變量塊。統一變量值可以組合爲統一變量塊。統一變量塊可以更高效地加載,也可在多個着色器程序間共享。
  • 佈局限定符。頂點着色器輸入可以用佈局限定符聲明,以顯式綁定着色器源代碼中的位置,而不需要調用API。佈局限定符也可以用於片段着色器的輸出,在渲染到多個渲染目標時將輸出綁定到各個目標。而且,佈局限定符可以用於控制統一變量塊的內存佈局。
  • 實例和頂點ID。頂點索引現在可以在頂點着色器中訪問,如果使用實例渲染,還可以訪問實例ID。
  • 片段深度。片段着色器可以顯式控制當前片段的深度值,而不是依賴深度值的插值。
  • 新的內建函數。引入了許多新的內建函數,以支持新的紋理功能、片段導數、半浮點數據轉換和矩陣及數學運算。
  • 寬鬆的限制。大大放鬆了對着色器的限制。着色器不再限於指令長度,完全支持變量爲基礎的循環和分支,並支持數組索引。

3、幾何形狀

  • 變換反饋。可以在緩衝區對象中捕捉頂點着色器的輸出。這對許多在GPU上執行動畫而不需要CPU干預的技術很實用,例如,粒子動畫或者使用“渲染到頂點緩衝區”的物理學模擬。
  • 布爾遮擋查詢。應用程序可以查詢一個(或者一組)繪製調用的任何像素是否通過深度測試。這個功能可以在各種技術中使用,例如鏡頭眩光效果的可見性確定,以及避免在邊界被遮擋的對象上進行幾何形狀處理的優化。
  • 實例渲染。有效地渲染包含類似幾何形狀但是屬性(例如變化矩陣、顏色或者大小)不同的對象。這一功能在渲染大量類似對象時很有用,例如人羣的渲染。
  • 圖元重啓。在OpenGL ES 2.0中爲新圖元使用三角形條帶時,應用程序必須在索引緩衝區中插入索引,以表示退化的三角形。在OpenGL ES 3.0中,可以還是要特殊的索引值表示新圖元的開始。這就消除了使用三角形條帶時生成退化三角形的需求。
  • 新頂點格式。支持包括10-10-10-2有符號和無符號規範化頂點屬性;8位、16位和32位整數屬性;以及16位半浮點。

4、緩衝區對象

  • 引入了許多新的緩衝區對象,以提高爲圖形管線各部分指定數據的效率和靈活性。
  • 統一變量緩衝區對象。爲存儲/綁定大的統一變量塊提供高效的方法。統一變量緩衝區對象可以減少將統一變量值綁定帶着色器的性能代價,這是OpenGL ES 2.0應用程序中的常見瓶頸。
  • 頂點數組對象。提供綁定和在頂點數組狀態之間切換的高效方法。頂點數組對象實際上是頂點數組狀態的容器對象。使用它們,應用程序可以在一次API調用中切換頂點數組狀態,而不是發出多個調用。
  • 採樣器對象。將採樣器狀態(紋理循環模式和過濾)與紋理對象分離。這爲在紋理中共享採樣器狀態提供了更高效的方法。
  • 同步對象。爲應用程序提供檢查一組操作是否在GPU上完成執行的機制。相關的新功能是柵欄(Fence),它爲應用程序提供了通知GPU應該等待一組操作結束才能接受更多操作進入執行隊列的方法。
  • 像素緩衝對象。使應用程序能夠執行對像素操作和紋理傳輸操作的異步數據傳輸。這種優化主要是爲了在CPU和GPU之間提供更快的數據傳輸,在傳輸操作期間,應用程序可以繼續工作。
  • 緩衝區子界映射。使應用程序能夠映射緩衝區的一個子區域,供CPU訪問。這可以提供比傳統緩衝區映射更好的性能,在傳統緩衝區映射中,必須使整個緩衝區可用於客戶。
  • 緩衝區對象間拷貝。提供了高效地從一個緩衝區對象向另一個緩衝區對象傳輸數據的機制,不需要CPU干預。

5、幀緩衝區

  • 增添了許多與屏幕外渲染帶幀緩衝區對象相關的新功能
  • 多重渲染目標(MRT)。允許應用程序同時渲染到多個顏色緩衝區。利用MRT技術,片段着色器輸出多個顏色,每個用於一個相連的顏色緩衝區。MRT用於許多高級的渲染算法,例如延遲着色。
  • 多重採樣渲染緩衝區。使應用程序能夠渲染到具備多重採樣抗鋸齒功能的屏幕外幀緩衝區。多重採樣幀緩衝區不能直接綁定到紋理,但是可以用新引入的幀緩衝區塊移動解析爲單採樣紋理。
  • 幀緩衝區失效提示。OpenGL ES 3.0的許多實現使用基於塊狀渲染(TBR)的GPU。TBR常常在必須爲了進一步渲染到幀緩衝區而毫無必要地恢復圖塊內容時導致很高的性能代價。幀緩衝區失效爲應用程序提供了通知驅動程序不再需要幀緩衝區內容的機制。這使驅動程序能夠採取優化步驟,跳過不必要的圖塊恢復操作。這一功能對於在許多應用程序中實現峯值性能很重要,特別是那些進行大量屏外渲染的程序。
  • 新的混合方程式。OpenGL ES 3.0支持最大值/最小值函數作爲混合方程式。

 

2:比如在如下的頂點着色器中定義了一個類型爲uniform的浮點型變量u_Time,我們就可以調用glGetUniformfv(program.program, program.getuTimeLocation(), floatBuffer)來時時獲取到它的值。

uniform mat4 u_Matrix;

/**  該值和a_ParticleStartTime相等  **/
uniform float u_Time;

/**  該值是在創建粒子發射器時傳入的,值不變,就是粒子最初始的位置  **/
attribute vec3 a_Position;

/**  該值是在創建粒子發射器時傳入的,值不變  **/
attribute vec3 a_Color;

/**  該值是在創建粒子發射器時傳入的,值不變,y方向0.5,x和z都是0  **/
attribute vec3 a_DirectionVector;

/**  該值等於(System.nanoTime() - globalStartTime) / 1000000000f  **/
attribute float a_ParticleStartTime;

/**  該值和a_Color相等,值不變  **/
varying vec3 v_Color;
varying float v_ElapsedTime;

void main() {
    v_Color = a_Color;
    v_ElapsedTime = u_Time - a_ParticleStartTime;
    vec3 currPosition = a_Position + (a_DirectionVector * v_ElapsedTime);
    gl_Position = u_Matrix * vec4(currPosition, 1.0);
    gl_PointSize = 10.0;
}

3:理解2.0中的varying類型,該類型我開始以爲是插值的意思,如下圖,兩點之間其他的點都是以起始和終止兩個點的顏色進行插值,其實不是,varying的作用就是把頂點着色器中定義的變量傳遞給片斷着色器,在3.0中把它改爲了out,這樣就非常形象了。

4:Matrix.rotateM(float[] m, int mOffset, float a, float x, float y, float z)方法是對第一個參數矩陣m進行旋轉,比如我要把矩陣m沿X方向旋轉60度,那麼就應該這樣調用:Matrix.rotateM(m, 0, 60, 1f, 0f, 0f);要把矩陣m沿Z軸旋轉-20度,那麼就應該這樣調用:Matrix.rotateM(m, 0, -20, 0f, 0f, 1f)。API會使用了,我們還必須要明白其中的道理,當我們要旋轉物體時,伸出右手,把大拇指對着旋轉目標軸的正方向,其他手指捲曲的方向就是旋轉的正方向,反之則是負方向。比如我們要沿X軸旋轉,那麼我們右手大拇指應該向右,掌心正對我們,向着我們身體的方向就是正方向,反着我們身體的則是負方向;比如我們要沿Y軸旋轉,那麼我們右手大拇指應該向上,掌心朝左側,朝左側轉動的方向就是旋轉的正方向,朝右側轉動的方向則是負方向。我們看看下面的示例圖就會更清楚了。

     第一幅圖是沒有任何旋轉的,我們要達到第二幅圖的樣子,可以判定,圖片是繞X軸旋轉的,伸出右手,拇指向右,圖片是向着我們的身體轉到這個角度的,和我們其他手指的方向相同,所以是正角度,這個角度的值是60度,所以我們應該這樣調用:Matrix.rotateM(modelMatrix, 0, 60f, 1f, 0f, 0f);再看第三幅圖,很明顯,它是背離我們身體的,和其他手指的方向相反,角度爲60度,所以我們應該這樣調用:Matrix.rotateM(modelMatrix, 0, -60f, 1f, 0f, 0f)。

5:在網上查找的Opengl ES的中文手冊,可供參考:OpenGL shader GLSL 中文手冊

6:點光和方向光在數學上都是相似的,然而,我們需要記住兩個關鍵的不同點:
1)對於方向光,我們只存儲指向其光源的向量,因爲這個向量對於場景中的所有點都是一樣的;對於點光則相反,我們將存儲其光源的位置,用這個位置計算場景中每個點指向這個光源的向量。
2)在實際生活中,點光源的亮度會隨着距離的平方降低,這叫作平方反比定律。我們將使用點光源的位置計算出其與場景中每個點的距離。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章