Unlimited Plugins, WordPress themes, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Code
  2. Android SDK
Code

如何在Android APP中使用OpenGL ES

by
Difficulty:AdvancedLength:LongLanguages:

Chinese (Traditional) (中文(繁體)) translation by tianyiliang (you can also view the original English article)

現在市面上幾乎每個Android手機都有一個圖形處理單元,或簡稱為GPU。顧名思義,就是專門處理與3D圖形計算相關的硬體單元。 作為一名APP開發人員,你可以利用GPU創建複雜的圖形和動畫,這些圖形和動畫可以以非常高的畫面播放速率運行。

目前有兩種不同的API可用於與Android設備的GPU交互:VulkanOpenGL ES。Vulkan僅適用於運行Android 7.0或更高版本的設備,而所有Android版本都支持OpenGL ES。

在本教程中,我將幫助你在Android APP中使用OpenGL ES 2.0。

準備工作

為了與本文一致,你需要:

  • 最新版本的Android Studio
  • 支援OpenGL ES 2.0或更高版本的Android設備
  • 最新版本的Blender,或任何其他3D建模軟體

1.什麼是OpenGL ES?

OpenGL是Open Graphics Library的縮寫,它是一個獨立於平臺的API,可以創建硬體加速的3D圖形。OpenGL ES是OpenGL for Embedded Systems的縮寫,是OpenGL API的一個子集。

OpenGL ES是一個非常低級的API。換句話說,它不提供任何讓你快速創建或操縱3D物件的方法。 相反,在使用它時,你需要手動管理任務,例如創建3D物件的各個頂點和麵,計算各種3D變換,以及創建不同類型的著色器。

還值得一提的是,Android SDK和NDK可以讓你在Java和C中編寫與OpenGL ES相關的代碼。

2.專案設置

因為OpenGL ES API是Android框架的一部分,所以不必為專案添加任何依賴關係就能夠使用它們。 但是在本教程中,我們將使用Apache Commons IO庫來讀取幾個文字檔的內容。因此,需要在build.gradle檔中的依賴項添加compile相關依賴:

此外,有些設備不支援OpenGL ES,無法安裝你的APP,為了阻止此類Google Play用戶,請將<uses-feature>添加到專案的清單檔中:

3.創建畫布

Android框架為3D圖形畫布提供了兩個控制項:GLSurfaceViewTextureView。 大多數開發人員喜歡使用GLSurfaceView,只有當他們打算在其他View控制項上迭加3D圖形時才選擇TextureView。這裡,我們選擇GLSurfaceView足夠了。

GLSurfaceView控制項添加到佈局檔。

請注意,我們已經將視窗的寬度與高度設置為相等。這樣做很重要,因為OpenGL ES坐標系是一個正方形。 如果你必須使用矩形畫布,請記住在計算投影矩陣時使用寬高比。你將在後面瞭解投影矩陣。

Activity類中通過findViewById()方法初始化GLSurfaceView控制項。

此外,我們必須調用setEGLContextClientVersion()方法來明確指定OpenGL ES版本。

4.創建3D對象

雖然可以通過手動編寫所有頂點的X,Y和Z座標來創建Java中的3D物件,但這樣做非常麻煩。   相反,使用3D建模工具更容易。Blender就是這樣的工具。它開源,功能強大,且非常容易學習。

啟動Blender並按X刪除默認立方體。接下來,按Shift-A並選擇 Mesh > Torus。我們現在有一個相當複雜的3D物件,由576個頂點組成。

Torus in Blender

為了能夠在Android APP中使用環面,我們必須將其匯出為Wavefront OBJ檔。因此,進入File > Export > Wavefront (.obj)。 在下一個螢幕中,給OBJ檔一個名稱,確保選擇了Triangulate FacesKeep Vertex Order選項,然後點擊Export OBJ按鈕。

Exporting 3D object as Wavefront OBJ file

你現在可以關閉Blender並將OBJ檔移動到Android Studio項目的assets資料夾。

5.解析OBJ文件

如果你還沒有注意到我們在上一步中創建的OBJ檔是一個文字檔,可以使用任何文字編輯器打開。

OBJ file opened with a text editor

在檔中,以“v”開頭的每一行代表單個頂點。類似地,以“f”開始的每一行表示單個三角形面。 當每個頂點線包含頂點的X,Y和Z座標時,每個麵線包含三個頂點的索引,它們一起形成一個面。這就是解析OBJ檔需要知道的一切。

在開始之前,創建一個名為Torus的新Java類,並添加兩個List物件作為成員變數,一個用於頂點,一個用於面。

讀取OBJ檔的所有行的最簡單方法是使用Scanner類及其nextLine()方法。當迴圈遍歷行並填充兩個列表時,你可以使用String類的startsWith()方法來檢查當前行是否以“v”或“f”開頭。

6.創建緩衝區對象

你不能將頂點和麵的列表直接傳遞給OpenGL ES API中的方法。你必須先將其轉換為緩衝物件。 要存儲頂點座標資料,我們需要一個FloatBuffer物件。對於僅由頂點索引組成的面部資料,ShortBuffer物件就足夠了。

因此,將以下成員變數添加到Torus類中:

要初始化緩衝區,我們必須首先使用allocateDirect()方法創建ByteBuffer物件。對於頂點緩衝區,為每個座標分配四個位元組,座標是浮點數。 一旦ByteBuffer物件被創建,你可以通過調用它的asFloatBuffer()方法將其轉換成FloatBuffer

同樣,為面緩衝區創建另一個ByteBuffer物件。這一次,為每個頂點索引配置兩個位元組,因為索引是unsigned short類型。 此外,請確保使用asShortBuffer()方法將ByteBuffer物件轉換為 ShortBuffer

填充頂點緩衝區需要迴圈遍歷verticesList內容,從每項中提取X,Y和Z座標,並調用put()將資料放入緩衝區。因為verticesList只包含字串,所以我們必須使用parseFloat()將座標從字串轉換為float值。

請注意,在上述代碼中,我們使用position()方法來重置緩衝區的位置。

填充面緩衝區略有不同。你必須使用parseShort()方法將每個頂點索引轉換為一個short類型的值。 另外,由於索引從一開始而不是從零開始,因此你必須記住在將它們放入緩衝區之前從它們中減一。

7.創建著色器

為了能夠渲染我們的3D物件,我們必須為它創建一個頂點著色器和片段著色器。現在,可以將著色器看成一個非常簡單的程式,用類似C語言的語言編寫,稱為OpenGL著色語言,簡稱GLSL。

機智如你,頂點著色器負責處理3D物件的頂點。片段著色器(也稱為圖元著色器)負責著色3D物件的圖元。

步驟1:創建頂點著色器

在項目的res/raw資料夾中創建一個名為vertex_shader.txt的新文件。

頂點著色器必須有一個attribute全域變數,以便從Java代碼接收頂點位置資料。此外,添加一個uniform全域變數以便從Java代碼接收視圖投影矩陣。

在頂點著色器的main()方法內部,你必須設置gl_position值,它是GLSL內置變數,該變數決定頂點的最終位置。現在,你可以簡單地將其值設置為的產品uniformattribute全域變數。

將以下代碼添加到檔中:

步驟2:創建片段著色器

在項目的res/raw資料夾中創建一個名為fragment_shader.txt的新文件。

為了簡化本教程,我們現在創建一個非常簡約的片段著色器,只需將橙色分配給所有圖元。要將一個顏色分配給一個圖元,在片段著色器的main()方法內部,使用gl_FragColor內置的變數。

在上面的代碼中,第一行指定浮點數精度很重要,因為片段著色器對沒有任何默認的精度。

第3步:編譯著色器

回到Torus類,你現在必須添加代碼來編譯你創建的兩個著色器。但是,在此之前,必須先將它們從原始資源轉換為字串。 IOUtils類(是Apache Commons IO 庫的一部分)有一個toString()可以做到。代碼如下:

著色器的代碼必須添加到OpenGL ES著色器物件中。要創建一個新的著色器物件,請使用GLES20類的glCreateShader()方法。 根據要創建的著色器物件的類型,你可以傳遞GL_VERTEX_SHADER或者GL_FRAGMENT_SHADER給它。該方法返回一個整數,該整數作為著色器物件的引用。 新創建的著色器物件不包含任何代碼。要將著色器代碼添加到著色器物件,必須使用glShaderSource()方法。

以下代碼是頂點著色器和片段著色器創建著色器對象:

我們現在可以將著色器物件傳遞給glCompileShader()方法來編譯它們包含的代碼。

8.創建一個程式

不要直接使用著色器渲染3D物件。而是將它們附加到程式然後再使用。因此,向Torus類添加一個成員變數用來存儲OpenGL ES程式的引用。

要創建一個新程式,請使用glCreateProgram()方法。要將頂點和片段著色器物件附加到它,請使用glAttachShader()方法。

此時,你可以連結程式並開始使用它。使用glLinkProgram()glUseProgram()方法來做。

9.繪製3D對象

隨著著色器和緩衝區的準備,我們有了繪製環面所需要的一切。在Torus類中添加一個新方法draw

在前面的步驟中,我們在頂點著色器內部定義了一個position變數來接收來自Java代碼的頂點位置資料。現在是將頂點位置資料發送給它的時候了。 為此,我們必須首先使用glGetAttribLocation()方法獲取Java代碼中的position變數的控制碼。此外,必須使用glEnableVertexAttribArray()方法啟用控制碼。

draw()方法中添加以下代碼:

我們必須使用glVertexAttribPointer()方法來將position控制碼指向我們的頂點緩衝區。除了頂點緩衝區本身之外,該方法還要求每個頂點的座標數,座標的類型以及每個頂點的位元組偏移量。 因為我們每個頂點有三個座標,每個座標是一個float類型,所以位元組偏移必須是3 * 4

我們的頂點著色器還需要一個視圖投影矩陣。雖然這樣的矩陣並不總是必需的,但使用它可以更好地控制3D物件的渲染方式。

視圖投影矩陣只是視圖和投影矩陣的乘積。視圖矩陣允許你指定相機的位置和它正在查看的點。  另一方面,投影矩陣不僅能讓你將OpenGL ES的平方坐標系映射到Android設備的矩形螢幕,還可以指定觀察 viewing frustum的近平面和遠平面。

要創建矩陣,可以簡單地創建三個float類型的陣列,每個陣列大小為16

要初始化投影矩陣,可以使用Matrix類的frustumM()方法。它需要左,右,底,頂,近和遠剪輯平面的位置。 因為我們的畫布已經是一個正方形,所以左和右的值為-1和1,頂和底的值也為-11。對於近和遠的剪輯平面,請隨時嘗試不同的值。

要初始化視圖矩陣,請使用setLookAtM()方法。它需要相機的位置和它正在看的點。你再次自由嘗試不同值。

最後,使用multiplyMM()方法計算產品矩陣。

要將產品矩陣傳遞給頂點著色器,你必須使用glGetUniformLocation()方法獲取matrix變數的控制碼。一旦有了控制碼,就可以使用glUniformMatrix()方法將其指向產品矩陣。

你要知道我們還沒有使用faces緩衝區。這意味著我們還沒有告訴OpenGL ES如何連接頂點來形成三角形,這將作為我們3D物件的面。

glDrawElements()方法讓你使用faces緩衝區來創建三角形。需要的參數有,頂點索引的總數,每個索引的類型和麵緩衝區。

最後,記得禁用attribute handler,之前將頂點資料傳遞給頂點著色器的時候被你打開了的。

10.創建渲染器

我們的GLSurfaceView控制項需要一個GLSurfaceView.Renderer物件來渲染3D圖形。可以使用setRenderer()將渲染器與其關聯。

在渲染器的onSurfaceCreated()方法內,你必須指定3D圖形呈現的頻率。現在,只有當3D圖形發生變化時,我們才能渲染。 為此,將RENDERMODE_WHEN_DIRTY常數傳遞給setRenderMode()方法。此外,初始化Torus物件的新實例。

在渲染器的onSurfaceChanged()方法內,可以使用glViewport()方法定義 viewport的寬度和高度。

在渲染器的onDrawFrame()方法內,調用Torus類的draw()方法來實際繪製圓環。

現在,運行應用程式來查看橙色圓環。

App displaying torus

結語

你現在知道如何在Android APP中使用OpenGL ES了。在本教程中,你還學到了如何解析Wavefront OBJ檔並從中提取頂點和麵資料。   我建議你使用Blender生成更多的3D物件,並嘗試在APP中渲染。

雖然我們只關注OpenGL ES 2.0,但是也要知道OpenGL ES 3.x向後相容了OpenGL ES 2.0。這意味著,如果你喜歡在你的APP中使用OpenGL ES 3.x中,你可以用類GLES30GLES31類簡單地替換GLES20類。

要瞭解有關OpenGL ES的更多資訊,可以參考其它頁面。要瞭解有關Android APP開發的更多資訊,請在Envato Tuts +上查看我們的其他教程!


关注我们的公众号
Advertisement
Advertisement
Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.