OpenGL ES->GLSurfaceView绘制图形的流程

avatar
作者
猴君
阅读量: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     } } 

总结

    1. 准备菱形的顶点数据:需要绘制的顶点数据存放在Float的数组中
    1. 分配顶点数据的直接字节缓冲区,使用 ByteBuffer 可以直接与底层硬件进行数据传输,避免了不必要的内存拷贝,从而提高性能
    • 常见内存拷贝图
      +--------------+          +--------------+          +--------------+ |   float[]    |  ---->   |  FloatBuffer |  ---->   |    GPU       | |  (Java Heap) |          |  (Java Heap) |          |   (Native)   | +--------------+          +--------------+          +--------------+ 
    • 使用 ByteBuffer
      +--------------+          +--------------+          +--------------+ |   float[]    |  ---->   | ByteBuffer   |  ---->   |    GPU       | |  (Java Heap) |          | (Direct)     |          |   (Native)   | +--------------+          +--------------+          +--------------+ 
    1. 创建顶点缓冲区对象(Vertex Buffer Object, VBO)
    1. 将顶点数据复制到缓冲区中
    1. 创建和编译顶点着色器程序
      // 定义顶点着色器代码的多行字符串 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()                   // 去除多行字符串的公共缩进 
    1. 创建和编译片段着色器程序
      // 定义片段着色器代码的多行字符串 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()                     // 去除多行字符串的公共缩进 
    1. 创建着色器程序, 将顶点着色器和片段着色器链接到一起
    1. 获取顶点数据的位置, 并使用该位置的数据
    1. 设置片段着色器的颜色
    1. 绘制菱形
    1. 禁用顶点数据
+---------------------+         +-------------------+         +------------------+ | Application (Java)  |  ---->  |   OpenGL Driver   |  ---->  |      GPU         | +---------------------+         +-------------------+         +------------------+ | float[] vertices    |         |   Bind VBO        |         |  Process VBO     | |                     |         |   Upload Data     |         |  Use Data        | +---------------------+         +-------------------+         +------------------+ 

    广告一刻

    为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!