你們好,這是個人OpenGL ES
高級進階系列文章,在個人github
上有一個與本系列文章對應的項目,歡迎關注,連接:github.com/kenneycode/…java
今天給你們介紹VBO(Vertex Buffer Object)
和IBO(Index Buffer Object)
,讓咱們先從一段代碼開始,逐步介紹它們:git
// 將三角形頂點數據放入buffer中
// Put the triangle vertex data into the vertexDataBuffer
vertexDataBuffer = ByteBuffer.allocateDirect(vertexData.size * java.lang.Float.SIZE / 8)
.order(ByteOrder.nativeOrder())
.asFloatBuffer()
vertexDataBuffer.put(vertexData)
vertexDataBuffer.position(0)
// 啓動對應位置的參數,這裏直接使用LOCATION_ATTRIBUTE_POSITION,而無需像OpenGL 2.0那樣須要先獲取參數的location
// Enable the parameter of the location. Here we can simply use LOCATION_ATTRIBUTE_POSITION, while in OpenGL 2.0 we have to query the location of the parameter
GLES30.glEnableVertexAttribArray(LOCATION_ATTRIBUTE_POSITION)
// 指定a_position所使用的頂點數據
// Specify the data of a_position
GLES30.glVertexAttribPointer(LOCATION_ATTRIBUTE_POSITION, VERTEX_COMPONENT_COUNT, GLES30.GL_FLOAT, false, 0, vertexDataBuffer)
複製代碼
這段代碼你們應該很熟悉了,它的做用就是將頂點數據指定給vertex shader
中的attribute
變量,這裏有個細節以前沒提到過,就是glVertexAttribPointer()
這個方法指定了頂點數據,但它不會將頂點數據一直存儲在顯存中,而咱們渲染的時候,全部要訪問的數據必需在顯存中,所以,每次渲染時,OpenGL
會有一個將這些頂點數據從內存複製到顯存的操做,這樣會帶來一些問題:github
一個是所以每次渲染都要複製一次,所以內存中的頂點數據要一直留着,否則複製的時候就沒有數據來複制了。ide
另外一個是若是頂點數據量大的時候,每次渲染都作這樣的一次複製,性能上會有問題,咱們的例子中,頂點算是很是少的,那何時頂點會多呢?例如作一些形變效果時,每每會劃分網格,通常來講劃分得越多,效果越細膩,這時頂點就會不少,另外,在作3D渲染時,一般頂點也不少,單個3D模型甚至能夠有上萬個頂點。post
那有什麼辦法來避免複製?這時就要用到VBO
,它能夠和頂點數據綁定,綁定後的頂點數據是一直存儲在顯存中的,當須要用這些頂點數據的時候,直接綁定這個VBO
就好了,不會有複製過程。性能
那IBO
又是什麼呢?它和VBO
做用很相似,VBO
是爲了不頂點數據的複製,IBO
是則是爲了不頂點索引數據的複製,什麼是頂點索引呢?咱們先來看一份頂點數據:優化
// 三角形頂點數據
// The vertex data of a triangle
private val vertexData = floatArrayOf(
-1f, -1f, // 左下角
-1f, 1f, // 左上角
1f, 1f, // 右上角
-1f, -1f, // 左下角
1f, 1f, // 右上角
1f, -1f // 右下角
)
複製代碼
這是一份很普通的頂點數據,在咱們的教程中屢次用到過,它用2個三角形組成了一個矩形,對應用GL_TRIANGLES
的繪製模式進行渲染(繪製模式可參考個人一篇文章《Android OpenGL ES 2.0 手把手教學(5)- 繪製模式》),咱們能夠很容易地看到,這6個頂點是有重複的,一個矩形只須要4個頂點就好了,有些點是不一樣三角形之間共用的,那麼如何讓不一樣三角形之間共用?這就要用到頂點索引,它能讓咱們用索引的方法告訴OpenGL
咱們的頂點,而不是每一個點都用座標的方式給出,這樣能夠減小咱們的頂點數據量,這在面片數量較大時頗有用。ui
那麼下面咱們來看看具體如何使用VBO
和IBO
,先來看看頂點數據和頂點索引數據:spa
// 三角形頂點、紋理數據
// The vertex data and texture coordinate data of triangles
private val vertexData = floatArrayOf(
-1f, -1f, 0f, 1f, // x, y, u, v
-1f, 1f, 0f, 0f,
1f, 1f, 1f, 0f,
1f, -1f, 1f, 1f
)
複製代碼
這裏咱們將頂點和紋理座標組合越來,這也是配合VBO
和IBO
的常規優化用法,它的好處讓頂點和紋理座標在存儲上靠近,利於OpenGL
取數據,提升性能,特別是在3D渲染時,數據通常都是這樣組織的。code
接下來用glGenBuffers
建立VBO
和IBO
:
// 建立VBO和IBO
// Create VBO and IBO
val buffers = IntArray(2)
GLES30.glGenBuffers(buffers.size, buffers, 0)
vbo = buffers[0]
ibo = buffers[1]
複製代碼
咱們能夠看到,在建立的時候其實並無VBO
和IBO
的區別,都是叫buffer
,它們是在真正用的時候才能體現出區別。
下面將頂點的紋理數據加載到VBO
中:
// 將頂點數據載入VBO
// Load vertex data into VBO
val vertexDataBuffer = ByteBuffer.allocateDirect(vertexData.size * java.lang.Float.SIZE / 8)
.order(ByteOrder.nativeOrder())
.asFloatBuffer()
vertexDataBuffer.put(vertexData)
vertexDataBuffer.position(0)
GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, vbo)
GLES30.glBufferData(GLES30.GL_ARRAY_BUFFER, vertexDataBuffer.capacity() * java.lang.Float.SIZE / 8, vertexDataBuffer, GLES30.GL_STATIC_DRAW)
GLES30.glEnableVertexAttribArray(LOCATION_ATTRIBUTE_POSITION)
GLES30.glEnableVertexAttribArray(LOCATION_ATTRIBUTE_TEXTURE_COORDINATE)
GLES30.glVertexAttribPointer(LOCATION_ATTRIBUTE_POSITION, 2, GLES30.GL_FLOAT, false, 16, 0)
GLES30.glVertexAttribPointer(LOCATION_ATTRIBUTE_TEXTURE_COORDINATE, 2, GLES30.GL_FLOAT, false, 16, 8)
複製代碼
主要的關鍵點是先用glBindBuffer
綁定咱們在操做的buffer
,這一點和操做texture
和frame buffer
時很像,操做前都先綁定。接着用glBufferData
給它喂數據,這裏最後一個參數用於提示OpenGL
以便於它作一些優化,例如這裏咱們傳了GL_STATIC_DRAW
,即咱們的數據是不會變的,還有一些其它的能夠設置,如GL_DYNAMIC_DRAW
則提示OpenGL
這個buffer
的數據是會變的。
在glVertexAttribPointer
指定頂點和紋理數據時,咱們再也不像以前那樣,直接把數據的傳進來,由於這時咱們的數據已經在VBO
中了,這裏不須要再傳,這裏重點關注最後兩個參數,倒數第二個參數是指定stride
,即OpenGL
去取一份數據時的跨度,這裏由於咱們把頂點和紋理數據組合在了一塊兒,所以一份數據是2個頂點和2個紋理座標,即4個float
,16個字節。倒數第一個參數是指定這個數據在一份數據中的開始位置,由於咱們在一份數據中是先放頂點再放紋理座標,所以對於頂點,開始位置是0,對於紋理座標,開始位置是第8個字節。
這樣咱們就設置好了VBO
和IBO
,在渲染的時候,直接綁定VBO
和IBO
就可使用對應的頂點和紋理數據了:
override fun onDrawFrame(gl: GL10?) {
// 設置清屏顏色
// Set the color which the screen will be cleared to
GLES30.glClearColor(0.9f, 0.9f, 0.9f, 1f)
// 清屏
// Clear the screen
GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT)
// 設置視口,這裏設置爲整個GLSurfaceView區域
// Set the viewport to the full GLSurfaceView
GLES30.glViewport(0, 0, glSurfaceViewWidth, glSurfaceViewHeight)
// 設置好狀態,準備渲染
// Set the status before rendering
GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, vbo)
GLES30.glBindBuffer(GLES30.GL_ELEMENT_ARRAY_BUFFER, ibo)
GLES30.glActiveTexture(GLES30.GL_TEXTURE0)
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, imageTexture)
// 調用draw方法用TRIANGLES的方式執行渲染,頂點數量爲3個
// Call the draw method with GL_TRIANGLES to render 3 vertices
GLES30.glDrawElements(GLES30.GL_TRIANGLES, indexData.size, GLES30.GL_UNSIGNED_INT, 0)
}
複製代碼
來看看效果,就是渲染出一張圖來,從效果上看不出什麼區別哈:
代碼在我github
的OpenGLESPro
項目中,本文對應的是SampleVBOAndIBO
,項目連接:github.com/kenneycode/…
感謝閱讀!