OpenGL(四) 提升Shader性能--VBO、EBO、VAO之VAO

什么是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
    }
}
### Android 平台上 OpenGLVBOEBOVAO 的用法 #### 一、VBO (Vertex Buffer Object) VBO 是用于存储大量顶点数据的对象。通过将顶点数据上传至 GPU 显存中,可以显著提高渲染效率。 ```java // 创建并绑定一个VBO int vbo; vbo = GLES20.glGenBuffers(); GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vbo); FloatBuffer vertexBuffer = ... // 初始化顶点缓冲区 GLES20.glBufferData( GLES20.GL_ARRAY_BUFFER, vertexBuffer.capacity() * Float.BYTES, vertexBuffer, GLES20.GL_STATIC_DRAW ); ``` 此代码片段展示了如何创建和初始化一个 VBO 来保存顶点坐标[^1]。 #### 二、EBO (Element Buffer Object 或 Index Buffer) EBO 存储的是索引列表,用来定义哪些顶点应该被连接起来形成几何图形。这有助于减少重复顶点的数量,从而节省内存空间并加快绘制速度。 ```java // 创建并填充EBO int ebo; ebo = GLES20.glGenBuffers(); GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, ebo); ShortBuffer indexBuffer = ... // 初始化索引缓冲区 GLES20.glBufferData( GLES20.GL_ELEMENT_ARRAY_BUFFER, indexBuffer.capacity() * Short.BYTES, indexBuffer, GLES20.GL_STATIC_DRAW ); ``` 上述代码说明了怎样构建 EBO 及其关联的数据结构。 #### 三、VAO (Vertex Array Object) VAO 负责管理多个 VBOs 和它们之间的配置关系。它记录了每个属性指针的位置以及相应的格式信息,使得切换不同类型的顶点布局变得简单快捷。 ```java // 设置VAO状态 int vao; vao = GLES20.glGenVertexArrays(); GLES20.glBindVertexArray(vao); // 配置顶点属性指针 GLES20.glEnableVertexAttribArray(0); // 启用第一个属性列 GLES20.glVertexAttribPointer( 0, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, vertexStride, offset ); // 解绑当前VAO GLES20.glBindVertexArray(0); ``` 这段程序描述了如何建立一个新的 VAO 实例,并对其进行必要的参数设定[^3]。 当准备实际绘图时: ```java // 绑定先前设置好的VAO GLES20.glBindVertexArray(vao); // 执行绘制命令 if(ebo != null){ GLES20.glDrawElements(primitiveType, count, type, indicesOffset); }else{ GLES20.glDrawArrays(primitiveType, firstIndex, count); } // 清理工作 GLES20.glBindVertexArray(0); ``` 以上就是针对 Android 设备上使用 OpenGL ES 进行三维图形编程时涉及到的三个重要概念及其具体实现方法[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值