文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Android中的OpenGL使用配置详解

2023-02-28 17:09

关注

引言

PS:虽然感觉好久没更新了,但是输入没有停止,短暂的停止是为了以后更好的更新,共勉。

前面有关 Android 音视频的渲染都是使用MediaCodec进行渲染,MediaCodec也有自己的弊端比如无法进行视频的编辑处理,而视频可以 OpenGL ES来进行渲染,可以很好进行处理,比如添加滤镜等,这里介绍下 Android 中 OpenGL,也就是 OpenGL  ES,它是免费、跨平台的、功能完善的 2D/3D 图形库接口 API,他针对多种嵌入式系统进行了专门设计,它是一个精心提取出来的 OpenGL 的子集,主要内容如下:

介绍

Android 可通过开放图形库 OpenGL ES 来支持高性能 2D 和 3D 图形,OpenGL 是一种跨平台的图形 API,用于为 3D 图形处理硬件指定标准的软件接口。OpenGL ES 是 OpenGL 规范的一种形式,适用于嵌入式设备,Android 支持多版 OpenGL ES API,各版本情况如下:

在 AndroidManifest.xml 中声明 OpenGL ES 的版本

<uses-feature android:glEsVersion="0x00020000" android:required="true" />

GLSurfaceView

GLSurfaceViewSurfaceViewOpenGL实现,从 Android 1.5 开始加入,在 SurfaceView的基础上添加了 EGL 的管理以及自带的渲染线程 GLThread,其主要功能如下:

EGL 窗口、OpenGL 表面、GL 表面含义都相同。

GLSurfaceView常用设置如下:

EGL配置

EGLConfigChooser的默认实现是SimpleEGLConfigChooser,默认情况下GLSurfaceView将选择深度缓冲深度至少为 16 位的PixelFormat.RGB_888格式的 surface,默认的EGLConfigChooser实现是SimpleEGLConfigChooser,具体如下:

private class SimpleEGLConfigChooser extends ComponentSizeChooser {
    public SimpleEGLConfigChooser(boolean withDepthBuffer) {
        super(8, 8, 8, 0, withDepthBuffer ? 16 : 0, 0);
    }
}

可以通过如下方式修改EGLConfig的默认行为:

// 设置默认EGLConfig的深度缓冲,true则为16位的深度缓冲
setEGLConfigChooser(boolean needDepth)
// 指定自定义的EGLConfigChooser
setEGLConfigChooser(android.opengl.GLSurfaceView.EGLConfigChooser configChooser)
// 指定各个分量的值
public void setEGLConfigChooser(int redSize, int greenSize, int blueSize,
            int alphaSize, int depthSize, int stencilSize)

渲染

通过setRenderer设置渲染器并启动渲染线程GLThread,渲染模式有两种如下:

渲染模式可以通过setRenderMode来进行设置,具体如下:

// 设置渲染器
public void setRenderer(Renderer renderer)
// 设置渲染模式,仅在setRenderer之后调用生效
public void setRenderMode(int renderMode)

setDebugFlags和setGLWrapper

setDebugFlags用于设置 Debug 标记,方便调试跟踪代码,可选值为DEBUG_CHECK_GL_ERRORDEBUG_LOG_GL_CALLSsetGLWrapper可以通过自定义GLWrapper来委托 GL 接口来添加一些自定义行为,具体如下:

// DEBUG_CHECK_GL_ERROR:每次GL调用都会检查,如果出现glError则会抛出异常
// DEBUG_LOG_GL_CALLS:以TAG为GLSurfaceView将日志记录在verbose级别的日志中
setDebugFlags(int debugFlags)
// 用于调试跟踪代码,可自定义GLWrapper包装GL接口并返回GL接口,可在
setGLWrapper(android.opengl.GLSurfaceView.GLWrapper glWrapper)

渲染器Renderer

这部分在前面提到过,这里单独说一下,要想在 GL 表面上执行渲染操作,需要实现Renderer对象完成实际渲染操作,通过如下方式给GLSurfaceView设置渲染器对象Renderer以及制定渲染模式,如下:

// 给GLSurfaceView设置渲染器对象Renderer
public void setRenderer(Renderer renderer)
// 设置渲染模式,仅在setRenderer之后调用生效
public void setRenderMode(int renderMode)

设置渲染器Renderer的时候,同时会创建独立线程GLThread并开启该线程,这个线程就是独立于 UI 线程的渲染线程。

这里就涉及到两个线程 UI 线程和渲染线程,自然涉及到线程之间的通信,可以使用 volatilesynchronized等实现线程之间的通信。

如果是在 UI 线程中调用渲染线程中的操作,可以使用GLSurfaceViewqueueEvent 方法来将该操作执行到渲染线程中,一般需要自定义GLSurfaceView的时候会用到,同样如果在渲染线程可以通过runOnUiThread来将与 UI 相关的操作执行到 UI 线程。

下面看下渲染器Reander的基本实现:

public class GLES20Renderer implements Renderer {
    private static final String TAG = GLES20Renderer.class.getSimpleName();
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        Log.i(TAG, "onSurfaceCreated");
        GLES20.glClearColor(0.0f, 0.0f, 1.0f, 1);
    }
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        Log.i(TAG, "onSurfaceChanged");
        GLES20.glViewport(0, 0, width, height);
    }
    public void onDrawFrame(GL10 gl) {
        Log.i(TAG, "onDrawFrame");
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
    }
}

坐标映射

先来了解下 OpenGL 的世界坐标系和与之对应的 Android 上的纹理坐标系,如下图所示:

在 Android 中使用 OpenGL 就要进行相应坐标的转换,下面看下 OpenGL 坐标系在 Android 屏幕中的映射关系,如下图所示:

如上图所示,左侧是默认的 OpenGL 坐标系,右侧是 OpenGL 坐标系在 Android 屏幕上的映射,可以明显看到图中的三角形是变形了的,为了保证图像比例就需要应用 OpenGL 投影模式和相机视图来转换坐标,这就涉及到投影矩阵和视图矩阵,这部分内容会在后续的文章中介绍。

绘制三角形

通过以上内容,Android OpenGL 算是初步入门了,按照习惯来个小案例,这里使用 OpenGL 绘制一个三角形,如下Triangle是三角形数据封装及着色器的的使用,后续渲染直接调用draw方法进行渲染绘制,如下:

// Triangle
class Triangle(context: Context) {
    companion object {
        // 坐标数组中每个顶点的坐标数
        private const val COORDINATE_PER_VERTEX = 3
    }
    private var programHandle: Int = 0
    private var positionHandle: Int = 0
    private var colorHandler: Int = 0
    private var vPMatrixHandle: Int = 0
    private var vertexStride = COORDINATE_PER_VERTEX * 4
    // 三角形的三条边
    private var triangleCoordinate = floatArrayOf(     // 逆时针的顺序的三条边
        0.0f, 0.5f, 0.0f,      // top
        -0.5f, -0.5f, 0.0f,    // bottom left
        0.5f, -0.5f, 0.0f      // bottom right
    )
    // 颜色数组
    private val color = floatArrayOf(0.63671875f, 0.76953125f, 0.22265625f, 1.0f)
    private var vertexBuffer: FloatBuffer =
        // (number of coordinate values * 4 bytes per float)
        ByteBuffer.allocateDirect(triangleCoordinate.size * 4).run {
            // ByteBuffer使用本机字节序
            this.order(ByteOrder.nativeOrder())
            // ByteBuffer to FloatBuffer
            this.asFloatBuffer().apply {
                put(triangleCoordinate)
                position(0)
            }
        }
    init {
        // read shader sourceCode
        val vertexShaderCode = GLUtil.readShaderSourceCodeFromRaw(context, R.raw.vertex_shader_triangle_default)
        val fragmentShaderCode =
            GLUtil.readShaderSourceCodeFromRaw(context, R.raw.fragment_shader_triangle)
        if (vertexShaderCode.isNullOrEmpty() || fragmentShaderCode.isNullOrEmpty()) {
            throw RuntimeException("vertexShaderCode or fragmentShaderCode is null or empty")
        }
        // compile shader
        val vertexShaderHandler = GLUtil.compileShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode)
        val fragmentShaderHandler =
            GLUtil.compileShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode)
        // create and link program
        programHandle = GLUtil.createAndLinkProgram(vertexShaderHandler, fragmentShaderHandler)
    }
    
    fun draw(mvpMatrix: FloatArray) {
        GLES20.glUseProgram(programHandle)
        // 获取attribute变量的地址索引
        // get handle to vertex shader's vPosition member
        positionHandle = GLES20.glGetAttribLocation(programHandle, "vPosition").also {
            // enable vertex attribute,默认是disable
            GLES20.glEnableVertexAttribArray(it)
            GLES20.glVertexAttribPointer(
                it, // 着色器中第一个顶点属性的位置
                COORDINATE_PER_VERTEX,
                GLES20.GL_FLOAT,
                false,
                vertexStride, // 连续的顶点属性组之间的间隔
                vertexBuffer
            )
        }
        // get handle to fragment shader's vColor member
        colorHandler = GLES20.glGetUniformLocation(programHandle, "vColor").also {
            GLES20.glUniform4fv(it, 1, color, 0)
        }
        // draw triangle
        GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, triangleCoordinate.size / COORDINATE_PER_VERTEX)
        GLES20.glDisableVertexAttribArray(positionHandle)
    }
}

渲染器实现如下:

// 渲染器实现
class MRenderer(private var context: Context) : GLSurfaceView.Renderer {
    private val tag = MRenderer::class.java.simpleName
    private lateinit var triangle: Triangle
    private val vPMatrix = FloatArray(16) // 模型视图投影矩阵
    private val projectionMatrix = FloatArray(16)
    private val viewMatrix = FloatArray(16)
    override fun onSurfaceCreated(gl: GL10?, config: EGLConfig?) {
        // 创建Surface时调用,在渲染开始时调用,用来创建渲染开始时需要的资源
        Log.d(tag, "onSurfaceCreated")
        triangle = Triangle(context)
    }
    override fun onSurfaceChanged(gl: GL10?, width: Int, height: Int) {
        // Surface改变大小时调用,设置视口
        Log.d(tag, "onSurfaceChanged")
        GLES20.glViewport(0, 0, width, height)
    }
    override fun onDrawFrame(gl: GL10?) {
        // 绘制当前frame,用于渲染处理具体的内容
        Log.d(tag, "onDrawFrame")
        triangle.draw(vPMatrix)
    }
}

上面都是基本的绘制操作,没啥好说的,其中着色器的使用流程会在后续文章中进行介绍,这里就不贴其他代码了,感兴趣的可以直接在文末查看源代码。

绘制效果

上面的绘制没有使用投影矩阵和相机视图来进行坐标转换,当横竖屏切换到时候会到导致变形,这个会在下篇文章中进行修正,看下上述代码绘制的效果图,如下图所示:

以上就是Android中的OpenGL使用配置详解的详细内容,更多关于Android OpenGL配置的资料请关注编程网其它相关文章!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-移动开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯