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/…

感谢阅读!


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

查看所有标签

猜你喜欢:

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

jQuery基础教程 (第4版)

jQuery基础教程 (第4版)

[美] Jonathan Chaffer、[美] Karl Swedberg / 李松峰 / 人民邮电出版社 / 2013-10 / 59.00

本书由jQuery API网站维护者亲自撰写,第一版自2008上市以来,一版再版,累计重印14次,是国内首屈一指的jQuery经典著作! 作为最新升级版,本书涵盖jQuery 1.10.x和jQuery 2.0.x。本书前6章以通俗易懂的方式讲解了jQuery的核心组件,包括jQuery的选择符、事件、动画、DOM操作、Ajax支持等。第7章和第8章介绍了jQuery UI、jQuery M......一起来看看 《jQuery基础教程 (第4版)》 这本书的介绍吧!

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具

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

HSV CMYK互换工具