OpenGL ES 高级进阶:VBO和IBO

栏目: 后端 · 发布时间: 5年前

内容简介:大家好,这是我的今天给大家介绍这段代码大家应该很熟悉了,它的作用就是将顶点数据指定给

大家好,这是我的 OpenGL ES 高级进阶系列文章,在我的 github 上有一个与本系列文章对应的项目,欢迎关注,链接: github.com/kenneycode/…

今天给大家介绍 VBO(Vertex Buffer Object)IBO(Index Buffer Object) ,让我们先从一段代码开始,逐步介绍它们:

// 将三角形顶点数据放入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 会有一个将这些顶点数据从内存复制到显存的操作,这样会带来一些问题:

  • 一个是因此每次渲染都要复制一次,因此内存中的顶点数据要一直留着,不然复制的时候就没有数据来复制了。

  • 另一个是如果顶点数据量大的时候,每次渲染都做这样的一次复制,性能上会有问题,我们的例子中,顶点算是非常少的,那什么时候顶点会多呢?例如做一些形变效果时,往往会划分网格,一般来说划分得越多,效果越细腻,这时顶点就会很多,另外,在做3D渲染时,通常顶点也很多,单个3D模型甚至可以有上万个顶点。

那有什么办法来避免复制?这时就要用到 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 我们的顶点,而不是每个点都用坐标的方式给出,这样可以减少我们的顶点数据量,这在面片数量较大时很有用。

那么下面我们来看看具体如何使用 VBOIBO ,先来看看顶点数据和顶点索引数据:

// 三角形顶点、纹理数据
// 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
                        )
复制代码

这里我们将顶点和纹理坐标组合越来,这也是配合 VBOIBO 的常规优化用法,它的好处让顶点和纹理坐标在存储上靠近,利于 OpenGL 取数据,提高性能,特别是在3D渲染时,数据一般都是这样组织的。

接下来用 glGenBuffers 创建 VBOIBO :

// 创建VBO和IBO
// Create VBO and IBO
val buffers = IntArray(2)
GLES30.glGenBuffers(buffers.size, buffers, 0)
vbo = buffers[0]
ibo = buffers[1]
复制代码

我们可以看到,在创建的时候其实并没有 VBOIBO 的区别,都是叫 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 ,这一点和操作 textureframe buffer 时很像,操作前都先绑定。接着用 glBufferData 给它喂数据,这里最后一个参数用于提示 OpenGL 以便于它做一些优化,例如这里我们传了 GL_STATIC_DRAW ,即我们的数据是不会变的,还有一些其它的可以设置,如 GL_DYNAMIC_DRAW 则提示 OpenGL 这个 buffer 的数据是会变的。

glVertexAttribPointer 指定顶点和纹理数据时,我们不再像之前那样,直接把数据的传进来,因为这时我们的数据已经在 VBO 中了,这里不需要再传,这里重点关注最后两个参数,倒数第二个参数是指定 stride ,即 OpenGL 去取一份数据时的跨度,这里因为我们把顶点和纹理数据组合在了一起,因此一份数据是2个顶点和2个纹理坐标,即4个 float ,16个字节。倒数第一个参数是指定这个数据在一份数据中的开始位置,因为我们在一份数据中是先放顶点再放纹理坐标,因此对于顶点,开始位置是0,对于纹理坐标,开始位置是第8个字节。

这样我们就设置好了 VBOIBO ,在渲染的时候,直接绑定 VBOIBO 就可以使用对应的顶点和纹理数据了:

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)

}
复制代码

来看看效果,就是渲染出一张图来,从效果上看不出什么区别哈:

OpenGL ES 高级进阶:VBO和IBO

代码在我 githubOpenGLESPro 项目中,本文对应的是 SampleVBOAndIBO ,项目链接: github.com/kenneycode/…

感谢阅读!


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Python Algorithms

Python Algorithms

Magnus Lie Hetland / Apress / 2010-11-24 / USD 49.99

Python Algorithms explains the Python approach to algorithm analysis and design. Written by Magnus Lie Hetland, author of Beginning Python, this book is sharply focused on classical algorithms, but it......一起来看看 《Python Algorithms》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

随机密码生成器
随机密码生成器

多种字符组合密码

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具