概述

  关于这篇文字会复习下OpenGLES(简称GL)的基本用法,相机和滤镜的开发会在下一篇文字中。

  开发一个相机应用,在Android中有很多办法,用GL来显示Camera,初看起来比较麻烦,如果了解了开发管线,便能很容易地理解GL程序,下面的代码会使用GL2.0语法,网上可以查到很多系统性介绍的资料。用GL实现相机的优点在于可以对图像数据编程,所以在操作图像上非常灵活,并且利用GPU多核心,并行处理的特点,效率上非常高,滤镜就可以很容易地通过GLSL实现。

介绍

uniform mat4 uMVPMatrix;
attribute vec4 a_Position;  
void main() {
    gl_Position = uMVPMatrix * a_Position; 
    gl_PointSize = 20.0; // gl_Position, gl_PointSize 这两个变量规定为着色器内部使用
}

  传入坐标的时候,GL的顶点坐标系统是一个-1到1的笛卡尔坐标系。GL纹理的坐标规则不同,
是(0, 1)的坐标系,顶点坐标和纹理坐标的对应关系是:

  (-1,-1,1) -> (0,1)

  (1,-1,1) -> (1,1)

  (-1,1,1) -> (0,0)

  (1,1,1) -> (1,0)

  需要谨记坐标系规则,传入顶点坐标绘制图形时,遵守该规则。

坐标


  ( Android手机设备的宽高不一,GL中假设了一个方形的Surface,坐标图中的第二个子图显示了当手机屏幕本身的宽高不是一个GL定义的标准方形的时候,展示出来的形状,通常遇到这个问题,解决方法就是在传入一个矩阵uMVPMatrix,用它和Vertices中的a_Position相乘,来使物体正常在屏幕上显示 )

  RGB,即为图像在程序中的抽象表示形式,使用它的时候,可以看做为一个3 * width * heigh维度的矩阵。共三层,每一层代表该颜色分量在每个像素中的值。即红绿蓝。一张图的像素数量可以为width * height位,每位代表一个像素,在一个像素的3层合成之后,便成为该像素要显示的颜色,参考这个页面

这篇文字会从最简单开始介绍:绘制三角形,绘制图片,绘制Camera这几步骤来接触GL开发 ,然后再来实现几个滤镜效果,会很有趣。

实现

public void onDrawFrame(GL10 glUnused) {
    GLES20.glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
    GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT);
}

效果

 程序用顶点着色器确定三角形的三个点位置,用片元着色器确定三角形内部的颜色,在使用过程中,需要Shader与GL之间通信,来传递顶点位置数据,根据代码,可以了解到对应的方法。

 程序流程中,第一步我们要初始化Shader代码,然后向Shader传送三角形的顶点坐标,最后让GL去绘制这三个点。

    // 存放顶点坐标的数组,X, Y, Z 对应GL坐标系的位置
    private static final float[] mTriangleVerticesData = {
        // X, Y, Z,
        0.2f, 0.2f, 0.0f,
        -0.2f, -0.2f, 0.0f,
        -0.2f, 0.2f, 0.0f,
        0.2f, -0.2f, 0.0f
    };

    @Override
    public void onDrawFrame(GL10 gl) {
        GLES20.glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
        GLES20.glClear( GLES20.GL_COLOR_BUFFER_BIT);
        GLES20.glUseProgram(mProgram);

        // 向shader中传递3个点的坐标
        mTriangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET);
        GLES20.glVertexAttribPointer(maPositionHandle, 3, GLES20.GL_FLOAT, false,
            TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mTriangleVertices);
        GLES20.glEnableVertexAttribArray(0);

        // 画一个三角,或者画四个点
        GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 3);
        //GLES20.glDrawArrays(GLES20.GL_POINTS, 0, 4);
    }

效果

因为要映射图像数据,我就需要在GL中将图像保存下来,GL提供了texture对象来给Shader使用,我们将图像保存到texture里。代码流程上,先声明一个texture,然后图像和这个texture绑定,改写片元着色器程序,使用sampler2D从栅格化后的图像中提取颜色值。

    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        // ...
        // 声明,并生成一个texture
        int[] textures = new int[1];
        GLES20.glGenTextures(1, textures, 0);
        // 绑定 texture
        mTextureID = textures[0];
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureID);
        //...
        // 将图像保存到tex中
        GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
    // Fragment Shader 片元着色器代码
    precision mediump float;
    varying vec2 vTextureCoord;
    uniform sampler2D sTexture;
    void main() {
        gl_FragColor = texture2D(sTexture, vTextureCoord * vec2(1.0, -1.0));
    }

效果

结尾

  总结了GL的基础概念和用法,GL能实现的效果很多,当然这不仅需要对GL API的了解,还需要掌握更多的数学工具,我觉得图形学是蛮有趣的,当然除了大学里枯燥的图形学课程以外:P

矩阵知识:

旋转:

x' = x + cos(a)
y' = y + sin(a)
z' = z

x = 2, y = 0;
a = 30'
x' = 2 + cos(a)
y' = 0 + sin(a)

sin(a) = 1/2
cos(a) = 0.9


###### 原代码:
https://github.com/alex943/blog_viewing_camera_with_gltexture_in_android

###### 参考:
https://developer.android.com/guide/topics/graphics/opengl
https://github.com/aosp-mirror/platform_development/blob/master/samples/ApiDemos/src/com/example/android/apis/graphics/GLES20Activity.java
https://github.com/aosp-mirror/platform_development/blob/master/samples/ApiDemos/src/com/example/android/apis/graphics/GLES20TriangleRenderer.java