环境:OpenGL ES3,Android Studio2022.2.1.19
简介
- OpenGL:Open Graphic Library,一个2D和3D的图形库,可以用于视频渲染,图像/视频编辑等。目前常见的图像库还有Vulkan,WebGPU,Metal,Direct3D,OpenGL ES等。
- OpenGL ES(OpenGL for Embedded Systems):针对手机等嵌入式设备设计的2D,3D图像库
在屏幕上绘制一个三角形
- 新建一个Triangle类:
import android.opengl.GLES30
import android.opengl.Matrix
import java.nio.ByteBuffer
import java.nio.ByteOrder
class Triangle {
private val vertexShaderCode = """
#version 300 es
uniform mat4 mTMatrix;
layout(location = 0) in vec4 vPosition;
void main() {
gl_Position = mTMatrix * vPosition;
}
""".trimIndent()
private val fragmentShaderCode = """
#version 300 es
precision mediump float; // 浮点数精度中等
uniform vec4 vColor;
out vec4 fragColor;
void main() {
fragColor = vColor;
}
""".trimIndent()
// 顶点数据 - 定义三角形的3个顶点
private val triangleCoords = floatArrayOf(
-0.5f, -0.5f, 0.0f, // 左下角
0.5f, -0.5f, 0.0f, // 右下角
0.0f, 0.5f, 0.0f // 正上方
)
// 索引数据 - 定义如何连接顶点形成三角形
private val indices = intArrayOf(
0, 1, 2
)
private val color = floatArrayOf(0.5f, 0.5f, 0.5f, 1.0f) // 灰色
private var translateMatrix = FloatArray(16) // 模型变换矩阵,可以实现让三角形旋转,平移缩放等
// OpenGL 对象
private var vaoId = IntArray(1) // VAO
private var vboId = IntArray(1) // VBO
private var eboId = IntArray(1) // EBO
private var mProgram = 0
private var mTMatrixHandle = 0
private var mColorHandle = 0
init {
// 初始化变换矩阵为单位阵
Matrix.setIdentityM(translateMatrix, 0)
// 可以取消注释来测试变换
//Matrix.translateM(translateMatrix, 0, 0.5f, 0.0f, 0f)
//Matrix.rotateM(translateMatrix, 0, 45f, 0f, 0f, 1f)
setupShaders()
setupBuffers()
}
private fun setupShaders() {
// 编译着色器
val vertexShader = compileShader(GLES30.GL_VERTEX_SHADER, vertexShaderCode)
val fragmentShader = compileShader(GLES30.GL_FRAGMENT_SHADER, fragmentShaderCode)
// 创建着色器程序
mProgram = GLES30.glCreateProgram()
GLES30.glAttachShader(mProgram, vertexShader)
GLES30.glAttachShader(mProgram, fragmentShader)
GLES30.glLinkProgram(mProgram)
// 检查链接状态
val linkStatus = IntArray(1)
GLES30.glGetProgramiv(mProgram, GLES30.GL_LINK_STATUS, linkStatus, 0)
if (linkStatus[0] == 0) {
val info = GLES30.glGetProgramInfoLog(mProgram)
GLES30.glDeleteProgram(mProgram)
throw RuntimeException("Program linking failed: $info")
}
// 清理着色器对象
GLES30.glDeleteShader(vertexShader)
GLES30.glDeleteShader(fragmentShader)
// 获取uniform位置,方便向shader程序传入值
mTMatrixHandle = GLES30.glGetUniformLocation(mProgram, "mTMatrix")
mColorHandle = GLES30.glGetUniformLocation(mProgram, "vColor")
}
private fun setupBuffers() {
// 1. 生成并绑定VAO
GLES30.glGenVertexArrays(1, vaoId, 0)
GLES30.glBindVertexArray(vaoId[0])
// 2. 设置顶点数据到VBO
val vertexBuffer = ByteBuffer.allocateDirect(triangleCoords.size * 4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer()
.apply {
put(triangleCoords)
position(0)
}
GLES30.glGenBuffers(1, vboId, 0)
GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, vboId[0])
// 将顶点数据从CPU传送到GPU
GLES30.glBufferData(
GLES30.GL_ARRAY_BUFFER,
triangleCoords.size * 4,
vertexBuffer,
GLES30.GL_STATIC_DRAW
)
// 3. 设置顶点属性指针
GLES30.glVertexAttribPointer(
0, // layout location = 0
3, // 每个顶点的分量数 (x, y, z)
GLES30.GL_FLOAT, // 数据类型
false, // 是否归一化
3 * 4, // 步长 (3个float * 4字节)
0 // 偏移量
)
GLES30.glEnableVertexAttribArray(0)
// 4. 设置索引数据到EBO
val indexBuffer = ByteBuffer.allocateDirect(indices.size * 4)
.order(ByteOrder.nativeOrder())
.asIntBuffer()
.apply {
put(indices)
position(0)
}
GLES30.glGenBuffers(1, eboId, 0)
GLES30.glBindBuffer(GLES30.GL_ELEMENT_ARRAY_BUFFER, eboId[0])
GLES30.glBufferData(
GLES30.GL_ELEMENT_ARRAY_BUFFER,
indices.size * 4,
indexBuffer,
GLES30.GL_STATIC_DRAW
)
// 5. 解绑VAO
GLES30.glBindVertexArray(0)
// 可选:解绑其他缓冲区
GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0)
GLES30.glBindBuffer(GLES30.GL_ELEMENT_ARRAY_BUFFER, 0)
}
private fun compileShader(type: Int, shaderCode: String): Int {
val shader = GLES30.glCreateShader(type)
GLES30.glShaderSource(shader, shaderCode)
GLES30.glCompileShader(shader)
// 检查编译状态
val compileStatus = IntArray(1)
GLES30.glGetShaderiv(shader, GLES30.GL_COMPILE_STATUS, compileStatus, 0)
if (compileStatus[0] == 0) {
val info = GLES30.glGetShaderInfoLog(shader)
GLES30.glDeleteShader(shader)
throw RuntimeException("Shader compilation failed: $info")
}
return shader
}
fun draw() {
// 使用着色器程序
GLES30.glUseProgram(mProgram)
// 绑定VAO(这会自动绑定关联的VBO和EBO)
GLES30.glBindVertexArray(vaoId[0])
// 设置uniform变量
GLES30.glUniformMatrix4fv(mTMatrixHandle, 1, false, translateMatrix, 0)
GLES30.glUniform4fv(mColorHandle, 1, color, 0)
// 使用EBO绘制
GLES30.glDrawElements(
GLES30.GL_TRIANGLES, // 绘制模式
indices.size, // 索引数量
GLES30.GL_UNSIGNED_INT, // 索引类型
0 // 偏移量
)
// 解绑VAO
GLES30.glBindVertexArray(0)
}
fun release() {
// 删除OpenGL资源
GLES30.glDeleteVertexArrays(1, vaoId, 0)
GLES30.glDeleteBuffers(1, vboId, 0)
GLES30.glDeleteBuffers(1, eboId, 0)
GLES30.glDeleteProgram(mProgram)
}
}
- 在OpenGL上下文中进行三角形的绘制
import Triangle
import android.content.Context
import android.opengl.GLES30
import android.opengl.GLSurfaceView
import javax.microedition.khronos.egl.EGLConfig
import javax.microedition.khronos.opengles.GL10
class MGLSurfaceView : GLSurfaceView {
constructor(context: Context) : super(context) {
// 使用OpenGL ES3
setEGLContextClientVersion(3)
// 为当前view设置render
setRenderer(MRender())
}
}
class MRender : GLSurfaceView.Renderer{
private lateinit var triangle : Triangle
override fun onSurfaceCreated(gl: GL10?, config: EGLConfig?) {
// 清空屏幕的默认颜色
GLES30.glClearColor(0.0f, 0.5f, 0.0f, 1.0f);
triangle = Triangle()
}
override fun onSurfaceChanged(gl: GL10?, width: Int, height: Int) {
// 更新视口的大小
GLES30.glViewport(0, 0, width, height)
}
// 每一帧都调用
override fun onDrawFrame(gl: GL10?) {
//清空当前缓冲区的颜色缓冲区
GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT)
triangle.draw()
}
}
- 主类:
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
class MainActivity : AppCompatActivity() {
private lateinit var glView: MGLSurfaceView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
glView = MGLSurfaceView(this)
setContentView(glView)
}
}
浙公网安备 33010602011771号