在Android中使用OpenGL ES進行開發第(二)節:定義圖形


1、前期基礎知識儲備
筆者計劃寫三篇文章來詳細分析OpenGL ES基礎的同時也是入門關鍵的三個點:編程

①OpenGL ES是什麼?與OpenGL的關係是什麼?——概念部分數組

②使用OpenGLES繪製2D/3D圖形的第一步:定義圖形;——運用部分網絡

③使用OpenGL ES繪製出②步驟中定義好的圖形:——運用部分,難點所在性能

經過這三篇文章的分析,就像給萬丈高樓墊定了基石,萬丈高樓平地起,後面利用OpenGLES作各類效果,各類變換都是創建在這三步的圖形編程理解之上的。學習

在前面的一篇文章《在Android中使用OpenGL ES進行開發第(一)課:概念先行》中,筆者詳細的分析了OpenGL ES2.0的一些相關概念,而且簡單的實現了一個OpenGL ES繪製的過程,那麼今天本節文章的分析就在前文的基礎上,繼續分析使用OpenGL ES2.0進行圖形繪製入門的第二個關鍵點——圖形的定義。優化

(1)爲何要定義圖形?操作系統

這個問題問的好,OpenGL ES2.0是OpenGL的子集,而OpenGL是用來繪製圖形的API,那麼OpenGL ES2.0也確定是用來繪製圖形的,只不過繪製的場景放在了Android中。不論是二維的簡單圖形仍是複雜的三維圖像,其基本組成都是簡單的圖形。如同窗習繪製自定義 View 同樣,定義圖形的形狀是實現各類複雜的圖形的基礎。對象

(2)咱們能夠定義什麼樣的圖形?接口

在 OpenGL 的世界裏,咱們只能畫點、線、三角形,複雜的圖形都是由三角形構成的。內存

點和直線能夠用於某些效果,可是隻有三角形才能用來構建擁有複雜的對象和紋理的場景。在OpenGL裏,咱們把單獨的點放在一個組裏用於構建三角形,再告訴OpenGL如何鏈接這些點。

(3)定義圖形的步驟——核心重點

①定義三角形頂點的座標數據的浮點型緩衝區FloatBuffer;

②建立一個數組triangleCoords[],裏面定義三角形三個頂點的座標;

③定義一個構造器,裏面實現三個邏輯:

1)初始化形狀中頂點座標數據的字節緩衝區ByteBuffer;

2)從 ByteBuffer 中得到一個基本類型緩衝區即浮點緩衝區FloatBuffer;

3)把座標數組triangleCoords[]放入到FloatBuffer中,並定義讀取順序;

 

2、上代碼,具體實現,以三角形的繪製爲例
首先咱們新建一個三角形類(public class Triangle {}),而後按照上文中提到的四個步驟,來具體看一下代碼:

第一步:定義三角形頂點的座標數據的浮點型緩衝區FloatBuffer;

// 爲頂點座標建立一個浮點型緩衝區
private FloatBuffer vertexBuffer;
頂點座標咱們通常是用float類型的數據指定,因此這裏指定一個浮點型緩衝區。

第二步:建立一個數組triangleCoords[],裏面定義三角形三個頂點的座標;

// 座標數組中的頂點座標個數
static final int COORDINATES_PRE_VERTEX = 3;
// 以逆時針順序,分別指定座標頂點座標,
// 每一個頂點須要指定三個座標,分別是X、Y、Z座標軸方向的數據;
// OpenGL 只會渲染座標值範圍在 [-1, 1] 的內容
static float triangleCoords[] = {
0.0f, 1.0f, 0.0f, // top 屏幕頂端中心點
-1.0f, -1.0f, 0.0f, // bottom left 屏幕底部左下角
1.0f, -1.0f, 0.0f // bottom right 屏幕底部右下角
//以上座標z都爲0 建立一個平面的三角形
};
這裏,咱們須要分別定義三個頂點的座標,每一個座標須要指定三個三個參數,分別對應XYZ座標軸方向的數據。

注意:當咱們繪製三角形時老是以逆時針的順序排列頂點;這稱爲「捲曲順序」。由於在任何地方都使用這種一致的捲曲順序,能夠優化性能:使用捲曲順序能夠指出一個三角形屬於任何給定物體的前面或者後面,OpenGL能夠忽略那些不管如何都沒法被看到的後面的三角形。

更多OpenGl ES2.0頂點座標的精彩內容,

建議參考《Android OpenGl ES2.0編程_相關概念與繪製頂點》

第三步:構造器內的三重邏輯:

難點:咱們已經完成了頂點的定義,可是,在OpenGL能夠存取它們以前,咱們仍然須要完成另外一步。主要的問題是這些代碼運行的環境與OpenGL運行的環境使用不一樣的語言。運行在Dalvik虛擬機上的代碼不能直接訪問本地環境,而OpenGL做爲本地系統又是直接運行在硬件上的;因此這時咱們須要使用Java一個特殊的緩衝區類集合,它能夠分配本地內存塊,而且把Java的數據複製到本地內存。本地內存就能夠被本地環境存取,並且不受垃圾回收器的管控。

1)初始化形狀中頂點座標數據的字節緩衝區ByteBuffer;

// 初始化形狀中頂點座標數據的字節緩衝區
// 經過 ByteBuffer的allocateDirect()方法獲取到 ByteBuffer 實例
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(
// 頂點座標個數 * 座標數據類型 float 一個是 4 bytes
triangleCoords.length * 4
);
2)從 ByteBuffer 中得到一個基本類型緩衝區即浮點緩衝區FloatBuffer;

// 由於 ByteBuffer 是將數據移進移出通道的惟一方式使用,
// 這裏使用 「as」 方法從 ByteBuffer字節緩衝區中得到一個基本類型緩衝區即浮點緩衝區FloatBuffer
vertexBuffer = byteBuffer.asFloatBuffer();
3)把座標數組triangleCoords[]放入到FloatBuffer中,並定義讀取順序;

// 把頂點座標信息數組triangleCoords[]存儲到 FloatBuffer
vertexBuffer.put(triangleCoords);
// 設置從緩衝區的第一個位置開始讀取頂點座標信息
vertexBuffer.position(0);
本節完整的代碼以下:

public class Triangle {

/**
* 定義三角形頂點的座標數據的浮點型緩衝區
*/
private FloatBuffer vertexBuffer;

// 座標數組中的頂點座標個數
static final int COORDINATES_PRE_VERTEX = 3;
static float triangleCoords[] = { // 以逆時針順序;
0.0f, 1.0f, 0.0f, // top
-1.0f, -1.0f, 0.0f, // bottom left
1.0f, -1.0f, 0.0f // bottom right
};

// Set color with red, green, blue and alpha (opacity) values
float color[] = { 0.63671875f, 0.76953125f, 0.22265625f, 1.0f };

public Triangle(){
// 初始化形狀中頂點座標數據的字節緩衝區
// 經過 allocateDirect 方法獲取到 DirectByteBuffer 實例
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(
// 頂點座標個數 * 座標數據類型 float 一個是 4 bytes
triangleCoords.length * 4
);

// 設置緩衝區使用設備硬件的本來字節順序進行讀取;
byteBuffer.order(ByteOrder.nativeOrder());
// ByteBuffer 是將數據移進移出通道的惟一方式,這裏使用 「as」 方法從 ByteBuffer 中得到一個基本類型緩衝區
vertexBuffer = byteBuffer.asFloatBuffer();
// 把頂點座標信息數組存儲到 FloatBuffer
vertexBuffer.put(triangleCoords);
// 設置從緩衝區的第一個位置開始讀取頂點座標信息
vertexBuffer.position(0);
}
}
3、上文字,具體解析代碼中的ByteBuffer


(1)ByteBuffer是什麼?

緩衝區(Buffer),緩衝區(Buffer)就是在內存中預留指定大小的存儲空間用來對輸入/輸出(I/O)的數據做臨時存儲,這部分預留的內存空間就叫作緩衝區。

在Java NIO(網絡接口對象)中,緩衝區的做用也是用來臨時存儲數據,能夠理解爲是I/O操做中數據的中轉站。緩衝區直接爲信道(Channel)服務,寫入數據到通道或從通道讀取數據,這樣的操利用緩衝區數據來傳遞就能夠達到對數據高效處理的目的。在NIO中主要有八種緩衝區類

 

可能讀者會有疑問?那爲何上面的代碼中要出現兩種緩衝區——FloatBuffer和ByteBufer,他們之間的關係是什麼?——讀者需記住:

①緩衝區是定長的,基本上它只是一個列表,它的全部元素都是基本數據類型,好比本例中的圖形頂點座標就是浮點型的數據,因此用浮點型緩衝區存儲浮點型的數據;

②緩衝區的存在是爲信道(Channel)服務的,而信道的讀寫方法只接收 ByteBuffer,即只有ByteBuffer這種類型的緩衝區能夠將數據移進和移出信道,因此全部的數據類型須要經過ByteBuffer做爲中轉,利用ByteBuffer讀寫其餘數據類型。

(2)ByteBuffer的實例方法

ByteBuffer有四種實例化方法,本例中使用了其中的一種——allocateDirect()。

①allocate(int capacity):從堆空間中分配一個容量大小爲capacity的byte數組做爲緩衝區的byte數據存儲器;

②allocateDirect(int capacity):經過操做系統來建立內存塊用做緩衝區,而不在JVM堆棧中;

③wrap(byte[] array):這個緩衝區的數據會存放在byte數組中;

④wrap(byte[] array,int offset, int length):在上一個方法的基礎上能夠指定偏移量和長度;

更多精彩ByteBuffer的精彩講解,建議讀者參考:《ByteBuffer經常使用方法詳解》

 

總結:本節中,主要分析瞭如何定義好三角形的頂點座標,須要作哪些事,關鍵的ByteBuffer如何理解,細心的讀者可能會發現,咱們這裏只是定義好了三角形的頂點座標,換句話說就是定義好了這個圖形,並無實際的繪製出來,整篇文章的代碼中都沒有出現draw()方法,那麼繪製圖形就無從談起。

相關文章
相關標籤/搜索