阅读量:0
自定义View
代码
class MyGLSurfaceView(context: Context, attrs: AttributeSet) : GLSurfaceView(context, attrs), GLSurfaceView.Renderer { var mProgrem = 0 init { // 设置 OpenGL ES 3.0 版本 setEGLContextClientVersion(3) // 设置当前类为渲染器, 注册回调接口的实现类 setRenderer(this) // 设置渲染模式, 仅在需要重新绘制时才进行渲染,以节省资源 renderMode = RENDERMODE_WHEN_DIRTY } 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() // 顶点着色器代码 val vertexShaderCode = """#version 300 es layout (location = 0) in vec4 aPosition; void main() { gl_Position = aPosition; }""".trimIndent() // 片段着色器代码 val fragmentShaderCode = """#version 300 es precision mediump float; uniform vec4 vColor; out vec4 fragColor; void main() { fragColor = vColor; }""".trimIndent() // 初始化着色器 mProgrem = initializeShaders(vertexShaderCode, fragmentShaderCode) } 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(mProgrem) } fun initializeBuffers(){ // 1. 准备菱形的顶点数据 val vertices = floatArrayOf( 0.0f, 0.5f, 0.0f, // 顶点 1 (顶部) -0.5f, 0.0f, 0.0f, // 顶点 2 (左侧) 0.5f, 0.0f, 0.0f, // 顶点 4 (右侧) 0.0f, -0.5f, 0.0f // 顶点 3 (底部) ) // 2. 分配顶点数据的直接字节缓冲区 val vertexBuffer = ByteBuffer.allocateDirect(vertices.size * 4) // 每个 float 占 4 个字节, 指定缓冲区所需要的字节数 .order(ByteOrder.nativeOrder()) // 指定字节顺序(确定是大端还是小端), 默认情况下为小端, 即低地址存放低位数据, 高地址存放高位数据 .asFloatBuffer() // 转换为FloatBuffer, 因为顶点数据是float类型 vertexBuffer.put(vertices) // 将顶点数据放入 FloatBuffer vertexBuffer.position(0) // 在将数据放入缓冲区后,位置指针会指向缓冲区的末尾。重置位置指针为 0,使得在后续操作中可以从缓冲区的开始位置读取数据 // 3. 创建顶点缓冲区对象(Vertex Buffer Object, VBO) val vbo = IntArray(1) GLES30.glGenBuffers(1, vbo, 0) // 生成一个缓冲区对象ID,并存储在数组 vbo 中,存放位置为0 GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, vbo[0]) // 绑定生成的顶点缓冲区对象,使其成为当前缓冲区操作的目标 // 4. 将顶点数据复制到缓冲区中 GLES30.glBufferData(GLES30.GL_ARRAY_BUFFER, vertices.size * 4, FloatBuffer.wrap(vertices), GLES30.GL_STATIC_DRAW) } fun drawSomething(program : Int){ // 8. 获取顶点数据的位置, 并使用该位置的数据 val positionHandle = GLES30.glGetAttribLocation(program, "aPosition") GLES30.glEnableVertexAttribArray(positionHandle) GLES30.glVertexAttribPointer(positionHandle, 3, GLES30.GL_FLOAT, false, 0, 0) // 9. 设置片段着色器的颜色 val colorHandle = GLES30.glGetUniformLocation(program, "vColor") GLES30.glUniform4f(colorHandle, 1.0f, 0.0f, 0.0f, 1.0f) // 红色 // 10. 绘制菱形 GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, 0, 4) // 11. 禁用顶点数据 GLES30.glDisableVertexAttribArray(positionHandle) } fun initializeShaders(vertexShaderCode: String, fragmentShaderCode: String) : Int { // 5. 创建和编译顶点着色器程序 val vertexShader = GLES30.glCreateShader(GLES30.GL_VERTEX_SHADER) GLES30.glShaderSource(vertexShader, vertexShaderCode) GLES30.glCompileShader(vertexShader) // 6. 创建和编译片段着色器程序 val fragmentShader = GLES30.glCreateShader(GLES30.GL_FRAGMENT_SHADER) GLES30.glShaderSource(fragmentShader, fragmentShaderCode) GLES30.glCompileShader(fragmentShader) // 7. 创建着色器程序, 将顶点着色器和片段着色器链接到一起 val program = GLES30.glCreateProgram() GLES30.glAttachShader(program, vertexShader) GLES30.glAttachShader(program, fragmentShader) GLES30.glLinkProgram(program) GLES30.glUseProgram(program) return program } }
总结
- 准备菱形的顶点数据:需要绘制的顶点数据存放在
Float
的数组中
- 准备菱形的顶点数据:需要绘制的顶点数据存放在
- 分配顶点数据的直接字节缓冲区,使用
ByteBuffer
可以直接与底层硬件进行数据传输,避免了不必要的内存拷贝,从而提高性能
- 常见内存拷贝图
+--------------+ +--------------+ +--------------+ | float[] | ----> | FloatBuffer | ----> | GPU | | (Java Heap) | | (Java Heap) | | (Native) | +--------------+ +--------------+ +--------------+
- 使用
ByteBuffer
+--------------+ +--------------+ +--------------+ | float[] | ----> | ByteBuffer | ----> | GPU | | (Java Heap) | | (Direct) | | (Native) | +--------------+ +--------------+ +--------------+
- 分配顶点数据的直接字节缓冲区,使用
- 创建顶点缓冲区对象
(Vertex Buffer Object, VBO)
- 创建顶点缓冲区对象
- 将顶点数据复制到缓冲区中
- 创建和编译顶点着色器程序
// 定义顶点着色器代码的多行字符串 val vertexShaderCode = """ #version 300 es // 指定使用 OpenGL ES 3.0 版本 layout (location = 0) in vec4 aPosition; // 定义输入变量 aPosition,并指定其位置为 0 void main() { // 主函数 gl_Position = aPosition; // 将输入的顶点位置赋值给 gl_Position,进行顶点变换 } """.trimIndent() // 去除多行字符串的公共缩进
- 创建和编译顶点着色器程序
- 创建和编译片段着色器程序
// 定义片段着色器代码的多行字符串 val fragmentShaderCode = """ #version 300 es // 指定使用 OpenGL ES 3.0 版本 precision mediump float; // 设置默认的浮点数精度为中等精度 uniform vec4 vColor; // 定义一个 uniform 变量 vColor,用于传递颜色 out vec4 fragColor; // 定义一个输出变量 fragColor,用于存储片段的颜色 void main() { // 主函数 fragColor = vColor; // 将 uniform 变量 vColor 的值赋给输出变量 fragColor } """.trimIndent() // 去除多行字符串的公共缩进
- 创建和编译片段着色器程序
- 创建着色器程序, 将顶点着色器和片段着色器链接到一起
- 获取顶点数据的位置, 并使用该位置的数据
- 设置片段着色器的颜色
- 绘制菱形
- 禁用顶点数据
+---------------------+ +-------------------+ +------------------+ | Application (Java) | ----> | OpenGL Driver | ----> | GPU | +---------------------+ +-------------------+ +------------------+ | float[] vertices | | Bind VBO | | Process VBO | | | | Upload Data | | Use Data | +---------------------+ +-------------------+ +------------------+