kotlin
复制代码
class MyRenderer : GLSurfaceView.Renderer {
private var mProgram = 0
private var vbo = IntArray(2) // 顶点缓冲区对象,需要存储位置数组,颜色数组
override fun onSurfaceCreated(gl: GL10?, config: EGLConfig?) {
// 当 Surface 创建时调用, 进行 OpenGL ES 环境的初始化操作, 设置清屏颜色为黑色 (Red=0, Green=0, Blue=0, Alpha=1)
GLES30.glClearColor(0.0f, 0.0f, 0.0f, 1.0f)
// 准备数据和缓存区,创建缓冲区对象并绑定数组对象
initializeBuffers()
// 创建并编译着色器程序
mProgram = initializeShaders()
}
override fun onSurfaceChanged(gl: GL10?, width: Int, height: Int) {
// 当 Surface 尺寸发生变化时调用,例如设备的屏幕方向发生改变, 设置视口为新的尺寸,视口是指渲染区域的大小
GLES30.glViewport(0, 0, width, height)
}
override fun onDrawFrame(gl: GL10?) {
// 每一帧绘制时调用, 清除颜色缓冲区和深度缓冲区
GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT or GLES30.GL_DEPTH_BUFFER_BIT)
// 绘制图形
drawSomething(mProgram)
}
private fun initializeBuffers() {
// 顶点数据
val vertices = floatArrayOf(
0.0f, 0.5f, 0.0f, // 顶点 1 (顶部)
-0.5f, 0.0f, 0.0f, // 顶点 2 (左侧)
0.5f, 0.0f, 0.0f // 顶点 3 (右侧)
)
// 颜色数据
val colors = floatArrayOf(
1.0f, 0.0f, 0.0f, // 顶点 1 的颜色 (红色)
0.0f, 1.0f, 0.0f, // 顶点 2 的颜色 (绿色)
0.0f, 0.0f, 1.0f // 顶点 3 的颜色 (蓝色)
)
// 分配顶点数据的缓冲区
val vertexBuffer = ByteBuffer.allocateDirect(vertices.size * 4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer()
.put(vertices)
.position(0)
// 分配颜色数据的缓冲区
val colorBuffer = ByteBuffer.allocateDirect(colors.size * 4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer()
.put(colors)
.position(0)
// 创建顶点缓冲区对象并绑定
GLES30.glGenBuffers(2, vbo, 0)
// 下面两个GLES30.glBindBuffer方法的调用
// 目的是将顶点数据和颜色数据上传到 GPU,并存储在缓冲区对象中,这个操作通常只需要执行一次,除非你需要更新缓冲区中的数据
// 绑定顶点缓冲区对象
GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, vbo[0])
GLES30.glBufferData(
GLES30.GL_ARRAY_BUFFER,
vertices.size * 4,
vertexBuffer,
GLES30.GL_STATIC_DRAW
)
// 绑定颜色缓冲区对象
GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, vbo[1])
GLES30.glBufferData(
GLES30.GL_ARRAY_BUFFER,
colors.size * 4,
colorBuffer,
GLES30.GL_STATIC_DRAW
)
}
// 准备着色器程序
private fun initializeShaders(): Int {
// 顶点着色器代码
val vertexShaderCode = """#version 300 es
layout (location = 0) in vec4 aPosition; // 输入顶点位置
layout (location = 1) in vec4 aColor; // 输入顶点颜色
out vec4 vColor; // 临时变量, 用于传递顶点颜色
void main() {
gl_Position = aPosition; // 输出顶点位置
vColor = aColor; // 输出顶点颜色
}""".trimIndent()
// 片段着色器代码
val fragmentShaderCode = """#version 300 es
precision mediump float; // 指定浮点精度
in vec4 vColor; // 输入顶点颜色
out vec4 fragColor; // 输出片段颜色
void main() {
fragColor = vColor; // 输出片段颜色
}""".trimIndent()
// 创建和编译顶点着色器程序
val vertexShader = GLES30.glCreateShader(GLES30.GL_VERTEX_SHADER)
GLES30.glShaderSource(vertexShader, vertexShaderCode)
GLES30.glCompileShader(vertexShader)
// 创建和编译片段着色器程序
val fragmentShader = GLES30.glCreateShader(GLES30.GL_FRAGMENT_SHADER)
GLES30.glShaderSource(fragmentShader, fragmentShaderCode)
GLES30.glCompileShader(fragmentShader)
// 创建着色器程序, 将顶点着色器和片段着色器链接到一起
val program = GLES30.glCreateProgram()
GLES30.glAttachShader(program, vertexShader)
GLES30.glAttachShader(program, fragmentShader)
GLES30.glLinkProgram(program)
return program
}
fun drawSomething(program: Int) {
GLES30.glUseProgram(program)
// 下面两个GLES30.glBindBuffer方法的调用
// 目的是在绘制时确保使用正确的缓冲区对象。每次绘制时,你都需要告诉 OpenGL ES 使用哪个缓冲区对象,这样它才能从这些缓冲区中读取数据进行绘制
// 绑定顶点缓冲区对象
GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, vbo[0])
val positionHandle = GLES30.glGetAttribLocation(program, "aPosition")
GLES30.glEnableVertexAttribArray(positionHandle)
GLES30.glVertexAttribPointer(positionHandle, 3, GLES30.GL_FLOAT, false, 0, 0)
// 绑定颜色缓冲区对象
GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, vbo[1])
val colorHandle = GLES30.glGetAttribLocation(program, "aColor")
GLES30.glEnableVertexAttribArray(colorHandle)
GLES30.glVertexAttribPointer(colorHandle, 3, GLES30.GL_FLOAT, false, 0, 0)
// 绘制
GLES30.glDrawArrays(GLES30.GL_TRIANGLES, 0, 3)
// 禁用顶点数据
GLES30.glDisableVertexAttribArray(positionHandle)
GLES30.glDisableVertexAttribArray(colorHandle)
}
}