OpenGl(四) 提升Shader性能--VBO、EBO、VAO之EBO
什么是EBO
EBO:Element Buffer Object 元素缓冲对象也被称为IBO,用于存储顶点索引值
优点:减少数据拷贝 ,可以更灵活的使用顶点数据,有一些效果使用EBO更方便
使用步骤
- 生成并绑定EBO
- 从GPU拷贝数据到EBO(GPU)
- 使用glDrawElements绘制图形
- 解绑EBO
相关API - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,……)
- glBufferData(GL_ELEMENT_ARRAY_BUFFER,……)
- glDrawElements.(该函数有4个参数,第一个参数,指定要绘制的图形,第二个参数,指定顶点个数,第三个参数,指定数据类型,第四个参数,指定偏移量)
使用EBO绘制三角形
import android.opengl.GLES30
import android.opengl.GLSurfaceView
import android.opengl.Matrix
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 DemoEBOGlRender : 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 translateMatrix = FloatArray(16)
private var program: Int? = null
private var positionHandle: Int = -1
private var colorHandle: Int = -1
private var tMatrixHandle: Int = -1
private var vertexBuffer: FloatBuffer? = null
private lateinit var byteBuffer: ByteBuffer
private var vboIds = IntArray(1)
private var eboIds = IntArray(1)
// private var indics = intArrayOf(0,1,2)
private var indics = intArrayOf(0, 1, 2)
override fun onSurfaceCreated(p0: GL10?, p1: EGLConfig?) {
// 设置背景颜色
GLES30.glClearColor(1f, 0f, 0f, 1.0f)
// 清理缓存
GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT)
createFloatBuffer()
val idxBuffer = ByteBuffer.allocateDirect(indics.size * 4).order(ByteOrder.nativeOrder()).asIntBuffer()
idxBuffer.put(indics).position(0)
Matrix.setIdentityM(translateMatrix, 0)
// 创建定点着色程序
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); // 立即释放vertexShader
GLES30.glDeleteShader(fragmentShader); // 立即释放fragmentShader
// VBO
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)
GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0)
// EBO
GLES30.glGenBuffers(1, eboIds, 0)
// glBindBuffer,绑定VBO到GPU (该函数有2个参数,target,指定存放的目标数据类型,它是常量,如GL_ARRAY_BUFFER表示用于存储顶点数据, int buffer,绑定的VBO对象ID,解绑定时将第二个参数设置为0)
GLES30.glBindBuffer(GLES30.GL_ELEMENT_ARRAY_BUFFER, eboIds[0])
// glBufferData,拷贝数据到VBO (该函数有4个参数,target,指定存放的目标数据类型,同glBindBuffer中的target,size指定拷贝数据的大小,data,从哪儿拷贝数据,usage,常量,指定拷贝数据的方式,一般设置为GL_STATIC_DRAW表示一次性拷贝)
// 注意*4
GLES30.glBufferData(GLES30.GL_ELEMENT_ARRAY_BUFFER,
idxBuffer.capacity() * Int.SIZE_BYTES,
idxBuffer,
GLES30.GL_STATIC_DRAW)
GLES30.glBindBuffer(GLES30.GL_ELEMENT_ARRAY_BUFFER, 0)
program?.let {
positionHandle = GLES30.glGetAttribLocation(it, "vPosition")
GLES30.glEnableVertexAttribArray(positionHandle)
// tMatrixHandle = GLES30.glGetUniformLocation(it, "mTMatrix")
colorHandle = GLES30.glGetUniformLocation(it, "vColor")
}
}
}
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)
GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, vboIds[0])
// 再次绑定VBO,表示从哪个VBO读取
GLES30.glBindBuffer(GLES30.GL_ELEMENT_ARRAY_BUFFER, eboIds[0])
// 最后一个参数设置为0时,会从GPU中读取数据
GLES30.glVertexAttribPointer(positionHandle, 3, GLES30.GL_FLOAT, false, 12, 0)
//将数据传递给shader
// GLES30.glUniformMatrix4fv(tMatrixHandle, 1, false, translateMatrix, 0)
// 设置三角形颜色,与程序中声明vColor变量名保持一致
GLES30.glUniform4fv(colorHandle, 1, color, 0)
// 绘制
GLES30.glDrawElements(GLES30.GL_TRIANGLES, indics.size, GLES30.GL_UNSIGNED_INT, 0)
// GLES30.glDisableVertexAttribArray(vPosition)
GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0)
GLES30.glBindBuffer(GLES30.GL_ELEMENT_ARRAY_BUFFER, 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
}
}
class TriangleView : GLSurfaceView {
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
init {
setEGLContextClientVersion(3)
setRenderer(DemoEBOGlRender())
renderMode = GLSurfaceView.RENDERMODE_WHEN_DIRTY
}
}