OpenGL ES->GLSurfaceView着色器程序中传递顶点数组和颜色数组绘制渐变三角形

自定义View代码

kotlin 复制代码
class MyGLSurfaceView(context: Context, attrs: AttributeSet) : GLSurfaceView(context, attrs) {
    init {
        // 设置 OpenGL ES 3.0 版本
        setEGLContextClientVersion(3)
        // 设置当前类为渲染器, 注册回调接口的实现类
        setRenderer(MyRenderer())
        // 设置渲染模式, 仅在需要重新绘制时才进行渲染,以节省资源
        renderMode = RENDERMODE_WHEN_DIRTY
    }
}

自定义Renderer渲染代码

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)
    }
}

效果图

相关推荐
戏谑9 分钟前
Android 常用布局
android·view
拭心12 小时前
Google 提供的 Android 端上大模型组件:MediaPipe LLM 介绍
android
带电的小王14 小时前
WhisperKit: Android 端测试 Whisper -- Android手机(Qualcomm GPU)部署音频大模型
android·智能手机·whisper·qualcomm
梦想平凡14 小时前
PHP 微信棋牌开发全解析:高级教程
android·数据库·oracle
元争栈道15 小时前
webview和H5来实现的android短视频(短剧)音视频播放依赖控件
android·音视频
阿甘知识库16 小时前
宝塔面板跨服务器数据同步教程:双机备份零停机
android·运维·服务器·备份·同步·宝塔面板·建站
元争栈道16 小时前
webview+H5来实现的android短视频(短剧)音视频播放依赖控件资源
android·音视频
MuYe16 小时前
Android Hook - 动态加载so库
android
居居飒17 小时前
Android学习(四)-Kotlin编程语言-for循环
android·学习·kotlin
Henry_He20 小时前
桌面列表小部件不能点击的问题分析
android