Android OpenGL 的基本使用

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android OpenGL 的基本使用相关的知识,希望对你有一定的参考价值。

参考技术A 由于本人现在在公司做android上的OpenGL图像处理相关功能,以前没有搞过这方面的知识,所以一切只能从头开始搞起,接下来将会慢慢分享其他方面的内容,先用这篇比较基础的文章来开头。

刚才我们谈到图像处理,在做图像处理我们不是可以用Canvas来绘制吗,怎么还要用OpenGL那么陌生的东西来搞?为什么要用OpenGL,肯定有它的好处。

接下来我们会来讲解如何在Android项目开发过程中加入OpenGL,在开始前我们先了解同OpenGL ES密切相关的载体:GLSurfaceView:

要用OpenGL绘制,首先要有GLSurfaceVie的实例

现在OpenGL ES版本已经到3.0了,Android平台上目前有1.0和2.0,我们使用的是2.0,在使用前在onCreate()方法中检查是否支持2.0的版本并且确定使用2.0

一般我们只需要使用“configurationInfo.reqGlEsVersion >= 0x20000”,至于加后面主要是用于模拟器检查,假定模拟器支持2.0。

前面说到GLSurfaceView挖了一个洞,就是为了看见下面的渲染表面,同样实在onCreate()方法中

通过setEGLContextClientVersion()方法配置surface视图,设定好使用的OpenGL版本,然后调用setRenderer()传进有自定义Renderer类的新实例。当Surface创建或者发生变化的时候,以及绘制一幅新帧时,渲染器都会被GLSurfaceView调用。

GLSurfaceView的生命周期要协同好Activity的生命周期,避免造成内存泄漏。

Renderer类也就是我们的渲染类了,它是通过实现Renderer接口来实现功能的。

渲染器接口定义的方法:

实现Renderer的接口方法

首选在onSurfaceCreated()中调用glClearColor设置清空屏幕用的颜色,这里使用红色。

设置视口的大小

在onDrawFrame()中调用glClear(GL_COLOR_BUFFER_BIT)清空屏幕,会调用glClearColor中定义的颜色来填充整个屏幕。通过这几个步骤,基本上就可以在GLSurfaceView绘制出东西了,在这里我只是简单的用红色绘制整个屏幕。

OpenGL在Android上的使用基本上是这样,但是,当然没那么简单,在使用OpenGL进行绘制算是比较繁琐的过程,后面也会慢慢去揭晓其他使用方法,来构造一幅一幅精美的特效静/动图。

Android OpenGL ES 学习 –矩阵变换

OpenGL 学习教程
Android OpenGL ES 学习(一) – 基本概念
Android OpenGL ES 学习(二) – 图形渲染管线和GLSL
Android OpenGL ES 学习(三) – 绘制平面图形
Android OpenGL ES 学习(四) – 正交投影
Android OpenGL ES 学习(五) – 渐变色
Android OpenGL ES 学习(六) – 使用 VBO、VAO 和 EBO/IBO 优化程序
Android OpenGL ES 学习(七) – 纹理
Android OpenGL ES 学习(八) –矩阵变换
Android OpenGL ES 学习(九) – 坐标系统和。实现3D效果
代码工程地址: https://github.com/LillteZheng/OpenGLDemo.git

今天要完成的效果:

一. 矩阵变换

说到矩阵,就不得不说大学的线性代数,可能大部分看到这,就不想往下看了。
别急,google 也知道我们懒,所以提供了 Matrix 这个类,来帮助我们,用简单易懂的方式,实现矩阵变换。
当然,一些基础知识,还是学习的,你也可以参考官网,学习更全面的知识。
https://learnopengl-cn.github.io/01%20Getting%20started/07%20Transformations/

1.1 矩阵基础知识

我们都知道 OpenGL 是一个向量,向量是什么东西,就是又有方向,又有大小,就像高中物理的力。

而矩阵又是什么呢?简单来说矩阵就是一个矩形的数字、符号或表达式数组。矩阵中每一项叫做矩阵的元素(Element)。下面是一个2×3矩阵的例子:

那向量跟矩阵又有啥关系?

我们知道,向量可以表示位置,颜色或者坐标,甚至是纹理,其实深入去看向量,它其实是一个 NX1的矩阵 ,N表示向量分量的个数(也叫N维(N-dimensional)向量:

这样,我们就可以让矩阵和向量相乘,而向量只有一列,故所得乘积的结果,依赖于矩阵的效果。正巧,很多有趣的2D/3D变换都可以放在一个矩阵中,用这个矩阵乘以我们的向量将变换(Transform)这个向量。

1.2 单位矩阵

单位矩阵是比较特殊的矩阵,在OpenGL 中,由于只有4个分量,所以使用 4x4 的矩阵,单位矩阵是一个除了对角线以外都是0的N×N矩阵,任何矩阵乘以它,都等于矩阵本身。


你可能会奇怪,要一个单位矩阵干嘛?别着急,看看下面的效果。

1.3 缩放

对一个向量进行缩放(Scaling)就是对向量的长度进行缩放,而保持它的方向不变。由于我们操作的是 2维或者3维,所以只需要改变x,y 或者 z 的大小就可以了。
比如向量v¯=(3,2)。我们可以把向量沿着x轴缩放0.5,使它的宽度缩小为原来的二分之一;我们将沿着y轴把向量的高度缩放为原来的两倍:

而用矩阵怎么表示呢?
如果把缩放值改成 s1,s2,s3 ,则缩放的矩阵为:

如果使用代码,则用:

Matrix.scaleM(UnitMatrix,0,s1,s2,s3)

1.4 平移

位移(Translation)是在原始向量的基础上加上另一个向量从而获得一个在不同位置的新向量的过程。
这个比较简单,套上矩阵的加法公式,可以得出矩阵:

使用代码:

Matrix.translateM(UnitMatrix,0,s1,s2,s3)

1.5 旋转

旋转稍微会比较复杂一点,如下图:

我们需要定义一条边,并在此基础上,旋转一个角度。那角度和方向怎么去关联呢,聪明的你,已经猜想到,使用 cosθ 或者 sinθ 了。旋转的矩阵,也是这样,如沿x轴旋转

代码:

Matrix.rotateM(UnitMatrix, 0, angle, s1, s2, s3);

二. 实践

之前的版本中,我们使用了正交投影 Android OpenGL ES 学习(四) – 正交投影,设置了一个矩阵,这样也保持不变,由于向量的读法是从右到左的,所以顶点着色器如下:

private const val VERTEX_SHADER = """#version 300 es
        uniform mat4 u_Matrix;
        layout(location = 0) in vec4 a_Position;
        layout(location = 1) in vec2 aTexture;
        out vec4 vTextColor;
        out vec2 vTexture;
        void main()
        
            // 矩阵与向量相乘得到最终的位置
            gl_Position = u_Matrix * a_Position;
            vTexture = aTexture;
        
        
        """

其他不变,然后获取 matrix 的值:

    override fun onSurfaceCreated(gl: GL10?, config: EGLConfig?) 
        GLES30.glClearColor(1f, 1f, 1f, 1f)
        makeProgram(VERTEX_SHADER, FRAGMENT_SHADER)

        uMatrix = getUniform(U_MATRIX)
        ...
    

为了每次都能修改都,将赋值操作,放到onDrawFrame 那里

override fun onDrawFrame(gl: GL10?) 
    //步骤1:使用glClearColor设置的颜色,刷新Surface
    GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT)

    GLES30.glUniformMatrix4fv(uMatrix, 1, false, UnitMatrix, 0)
    //useVaoVboAndEbo
    texture?.apply 
        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D,id)
    

    GLES30.glBindVertexArray(vao[0])
    GLES30.glDrawElements(GLES30.GL_TRIANGLE_STRIP, 6, GLES30.GL_UNSIGNED_INT, 0)

GlSurfaceView 的刷新模式,改成点击才刷新,这样方便点击测试

 //等待点击才会刷帧
renderMode = GLSurfaceView.RENDERMODE_WHEN_DIRTY

其中,平移,旋转和缩放的代码如下:

 linear.addBtn("平移")
   Matrix.translateM(UnitMatrix,0,0.5f,0.0f,0f)
    glView.requestRender()

linear.addBtn("旋转")
    Matrix.rotateM(UnitMatrix, 0, 180f, 1f, 0f, 0f);
    glView.requestRender()

linear.addBtn("缩放")
    Matrix.scaleM(UnitMatrix,0,0.8f,0.8f,0f)
    glView.requestRender()

看看效果:

以上是关于Android OpenGL 的基本使用的主要内容,如果未能解决你的问题,请参考以下文章

未能在 OpenGL/Android 中使用 VBO

Android OpenGL ES 学习 – 纹理

Android OpenGL ES 学习 – 纹理

Android Opengl FBO 屏幕外

Android OpenGL ES 学习 –矩阵变换

Android OpenGL ES 学习 –矩阵变换