什么是VAO
VAO:Vertex Array Object 顶点数组对象,它是在GPU分配的,存放VBO对象ID
VAO与VBO:
- VAO与VBO都是在GPU上分配空间
- VBO用于存放顶点数据
- VAO是VBO的管理者
- VAO中存放的是VBO的ID
VAO的好处: - 绘制不同物体时,不需要重置VBO
- 可以提高性能和效率
使用VAO
- 生成VAO并绑定GPU
- 生成并绑定一个或多个VBO
- VBO ID会自动保存到VAO中 (不需要手动处理,内部会自行绑定)
- 拷贝数据到VBO
- 连接VAO与Shader (不需要手动处理,内部执行)
相关API
- glGenVertexArrays,生成VAO ID(该函数有三个输入参数,n 指明产生几个VAO,arrays,存放产生的VAO ID, offset:一般写0)
- glBindVertextArray,绑定VAO (该函数有一个输入参数,array:指明绑定哪个VAO)
使用VAO绘制三角形
import android.opengl.GLES30
import android.opengl.GLSurfaceView
import android.util.Log
import java.nio.*
import javax.microedition.khronos.egl.EGLConfig
import javax.microedition.khronos.opengles.GL10
/**
* Day:2024/7/13 15:28
* @author wangdanfeng02
*/
class DemoVAOGlRender : 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 vPosition: Int = 0
private var vertexBuffer: FloatBuffer? = null
private lateinit var byteBuffer: ByteBuffer
private var vboIds = IntArray(1)
private var vaoIds = IntArray(1)
override fun onSurfaceCreated(p0: GL10?, p1: EGLConfig?) {
// 设置背景颜色
GLES30.glClearColor(1f, 0f, 0f, 1.0f)
// 清理缓存
GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT)
createFloatBuffer()
// 创建定点着色程序
val vertexShader = loadShader(GLES30.GL_VERTEX_SHADER, vertexShaderCode)
// 创建片元着色程序
val fragmentShader = loadShader(GLES30.GL_FRAGMENT_SHADER, fragmentShaderCode)
if (vertexShader != 0 && fragmentShader != 0) {
linkProgram(vertexShader, fragmentShader)
GLES30.glDeleteShader(vertexShader)
GLES30.glDeleteShader(fragmentShader)
// VAO
GLES30.glGenVertexArrays(0, vaoIds, 0)
GLES30.glBindVertexArray(vaoIds[0])
// VBO 需要在linkProgram之后再使用VBO,原因是管线渲染顺序
// glGenBuffer,生成VBO n表示一次生成多少个VBO,buffers,存放生成好的VBO对象的索引值,offset,目前没什么用,一般设置为0
GLES30.glGenBuffers(1, vboIds, 0)
// glBindBuffer,绑定VBO到GPU (该函数有2个参数,target,指定存放的目标数据类型,它是常量,如GL_ARRAY_BUFFER表示用于存储顶点数据, int buffer,绑定的VBO对象ID,解绑定时将第二个参数设置为0)
GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, vboIds[0])
// glBufferData,拷贝数据到VBO (该函数有4个参数,target,指定存放的目标数据类型,同glBindBuffer中的target,size指定拷贝数据的大小,data,从哪儿拷贝数据,usage,常量,指定拷贝数据的方式,一般设置为GL_STATIC_DRAW表示一次性拷贝)
GLES30.glBufferData(GLES30.GL_ARRAY_BUFFER, byteBuffer.capacity(), byteBuffer, GLES30.GL_STATIC_DRAW)
// 获取顶点着色器的vPosition,与程序中声明vPosition变量名保持一致
program?.let {
vPosition = GLES30.glGetAttribLocation(it, "vPosition")
GLES30.glEnableVertexAttribArray(vPosition)
// 最后一个参数设置为0时,会从GPU中读取数据
GLES30.glVertexAttribPointer(0, 3, GLES30.GL_FLOAT, false, 0, 0)
}
GLES30.glBindBuffer(GLES30.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 = GLES30.glCreateProgram()
program?.let {
// 将顶点着色器加入程序
GLES30.glAttachShader(it, vertexShader)
// 将片元着色器加入程序
GLES30.glAttachShader(it, fragmentShader)
// 链接到着色器程序
GLES30.glLinkProgram(it)
// 将程序加入到opengl30环境中
GLES30.glUseProgram(it)
val info = GLES30.glGetProgramInfoLog(it)
// 打印链接程序日志
Log.e("wdf", "info==" + info)
}
}
override fun onSurfaceChanged(p0: GL10?, width: Int, height: Int) {
// 设置绘制窗口
GLES30.glViewport(0, 0, width, height)
}
override fun onDrawFrame(p0: GL10?) {
program ?: return
if (program == 0) {
return
}
program?.let {
GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT)
GLES30.glUseProgram(it)
// 绑定VAO
GLES30.glBindVertexArray(vaoIds[0])
// 设置三角形颜色,与程序中声明vColor变量名保持一致
val vColor = GLES30.glGetUniformLocation(it, "vColor")
GLES30.glUniform4fv(vColor, 1, color, 0)
// 绘制
GLES30.glDrawArrays(GLES30.GL_TRIANGLES, 0, triangleCoors.size / 3)
// GLES30.glDisableVertexAttribArray(vPosition)
// GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0)
GLES30.glBindVertexArray(0)
}
}
/**
* 创建shader,加载shader程序
*/
private fun loadShader(type: Int, shaderCode: String): Int {
val shader = GLES30.glCreateShader(type)
GLES30.glShaderSource(shader, shaderCode)
GLES30.glCompileShader(shader)
return shader
}
fun onDestroy() {
GLES30.glDeleteBuffers(1,vboIds,0)
GLES30.glDeleteVertexArrays(1,vboIds,0)
GLES30.glDeleteProgram(program!!)
}
}
import android.content.Context
import android.opengl.GLSurfaceView
import android.util.AttributeSet
class TriangleView : GLSurfaceView {
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
init {
setEGLContextClientVersion(3)
setRenderer(DemoVAOGlRender())
renderMode = GLSurfaceView.RENDERMODE_WHEN_DIRTY
}
}