什么是VBO
Vertex Buffer Object顶点缓冲对象,用于在GPU中存储数据,加速渲染过程。
优点:(1)减少CPU到GPU数据的传输,提高渲染效率(举例渲染三角形,需要知道顶点+颜色,这些数据都在cpu,如果模型复杂,每次都需要时才拷贝数据,因此这里使用VBO会将数据进行一次性拷贝到GPU,使用时直接从GPU存储空间获取数据,进行渲染)
(2)数据保存在GPU,可重复使用
(3)可以直接在GPU对数据进行转换,减少GPU负载
使用VBO基本步骤
- 生成VBO对象并进行绑定
- 将顶点数据赋值到VBO对象中
- 绘制几何图元,从VBO中读取数据进行处理
- 解绑VBO对象(因为数据都在GPU中,因此需要使用之后进行释放GPU空间,否则会耗尽GPU空间)
相关API
- glGenBuffer,生成VBO (该函数有三个参数,n表示一次生成多少个VBO,buffers,存放生成好的VBO对象的索引值,offset,目前没什么用,一般设置为0)
- glBindBuffer,绑定VBO到GPU (该函数有2个参数,target,指定存放的目标数据类型,它是常量,如GL_ARRAY_BUFFER表示用于存储顶点数据, int buffer,绑定的VBO对象ID,解绑定时将第二个参数设置为0)
- glBufferData,拷贝数据到VBO (该函数有4个参数,target,指定存放的目标数据类型,同glBindBuffer中的target,size指定拷贝数据的大小,data,从哪儿拷贝数据,usage,常量,指定拷贝数据的方式,一般设置为GL_STATIC_DRAW表示一次性拷贝)
- glVerTexAttribPoint(…,0)从VBO获取数据,最后一个参数必须设置为0
代码示例
class DemoVBOGlRender : GLSurfaceView.Renderer {
/*
* 顶点位置程序
*/
private val vertexShaderCode =
"attribute vec4 vPosition;" +
"void main() { " +
" gl_Position = vPosition;" +
"}"
/**
* 片元颜色程序
*/
private val fragmentShaderCode =
"precision mediump float;" +
"uniform vec4 vColor;" +
"void main() { " +
" gl_FragColor = vColor;" +
"}"
/**
* 三角形顶点位置
*/
private val triangleCoors = floatArrayOf(
0.5f, 0.5f, 0.0f,
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f
)
/**
* 三角形颜色值
*/
private val color = floatArrayOf(
1.0f, 1.0f, 1.0f, 1.0f
)
private var program: Int? = null
private var vertexBuffer: FloatBuffer? = null
private lateinit var byteBuffer: ByteBuffer
private var vboIds = IntArray(1)
override fun onSurfaceCreated(p0: GL10?, p1: EGLConfig?) {
// 设置背景颜色
GLES20.glClearColor(1f, 0f, 0f, 1.0f)
// 清理缓存
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT)
createFloatBuffer()
// 创建定点着色程序
val vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode)
// 创建片元着色程序
val fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode)
if (vertexShader != 0 && fragmentShader != 0) {
linkProgram(vertexShader, fragmentShader)
// VBO 需要在linkProgram之后再使用VBO,原因是管线渲染顺序
// glGenBuffer,生成VBO n表示一次生成多少个VBO,buffers,存放生成好的VBO对象的索引值,offset,目前没什么用,一般设置为0
GLES20.glGenBuffers(1, vboIds, 0)
// glBindBuffer,绑定VBO到GPU (该函数有2个参数,target,指定存放的目标数据类型,它是常量,如GL_ARRAY_BUFFER表示用于存储顶点数据, int buffer,绑定的VBO对象ID,解绑定时将第二个参数设置为0)
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vboIds[0])
// glBufferData,拷贝数据到VBO (该函数有4个参数,target,指定存放的目标数据类型,同glBindBuffer中的target,size指定拷贝数据的大小,data,从哪儿拷贝数据,usage,常量,指定拷贝数据的方式,一般设置为GL_STATIC_DRAW表示一次性拷贝)
GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, byteBuffer.capacity(), byteBuffer, GLES20.GL_STATIC_DRAW)
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0)
}
}
private fun createFloatBuffer() {
// 申请物理层空间
byteBuffer = ByteBuffer.allocateDirect(triangleCoors.size * 4).apply {
this.order(ByteOrder.nativeOrder())
}
// 坐标数据转换
vertexBuffer = byteBuffer.asFloatBuffer()
vertexBuffer?.put(triangleCoors, 0, triangleCoors.size)
vertexBuffer?.position(0)
}
private fun linkProgram(vertexShader: Int, fragmentShader: Int) {
// 创建空的opengl es 程序
program = GLES20.glCreateProgram()
program?.let {
// 将顶点着色器加入程序
GLES20.glAttachShader(it, vertexShader)
// 将片元着色器加入程序
GLES20.glAttachShader(it, fragmentShader)
// 链接到着色器程序
GLES20.glLinkProgram(it)
// 将程序加入到opengl30环境中
GLES20.glUseProgram(it)
val info = GLES20.glGetProgramInfoLog(it)
// 打印链接程序日志
Log.e("wdf", "info==" + info)
}
}
override fun onSurfaceChanged(p0: GL10?, width: Int, height: Int) {
// 设置绘制窗口
GLES20.glViewport(0, 0, width, height)
}
override fun onDrawFrame(p0: GL10?) {
program ?: return
if (program == 0) {
return
}
program?.let {
GLES20.glClear(GLES30.GL_COLOR_BUFFER_BIT)
GLES20.glUseProgram(it)
// 再次绑定VBO,表示从哪个VBO读取
GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, vboIds[0])
// 获取顶点着色器的vPosition,与程序中声明vPosition变量名保持一致
val vPosition = GLES20.glGetAttribLocation(it, "vPosition")
GLES20.glEnableVertexAttribArray(vPosition)
// 3*4是指 跨3个读下一个顶点
// GLES20.glVertexAttribPointer(0, 3, GLES20.GL_FLOAT, false, 3 * 4, vertexBuffer)
// 最后一个参数设置为0时,会从GPU中读取数据
GLES20.glVertexAttribPointer(0, 3, GLES20.GL_FLOAT, false, 0, 0)
// 设置三角形颜色,与程序中声明vColor变量名保持一致
val vColor = GLES20.glGetUniformLocation(it, "vColor")
GLES20.glUniform4fv(vColor, 1, color, 0)
// 绘制
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, triangleCoors.size/3)
GLES20.glDisableVertexAttribArray(vPosition)
GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0)
}
}
/**
* 创建shader,加载shader程序
*/
private fun loadShader(type: Int, shaderCode: String): Int {
val shader = GLES20.glCreateShader(type)
GLES20.glShaderSource(shader, shaderCode)
GLES20.glCompileShader(shader)
return shader
}
}
class TriangleView : GLSurfaceView {
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
init {
setEGLContextClientVersion(2)
setRenderer(DemoVBOGlRender())
renderMode = GLSurfaceView.RENDERMODE_WHEN_DIRTY
}
}