前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android中基于OpenGL的特效

Android中基于OpenGL的特效

作者头像
Oceanlong
发布2018-08-14 17:33:06
2.1K0
发布2018-08-14 17:33:06
举报

前言

大家都知道,给图片加滤镜加特效,通常是对图像进行矩阵运算。通过颜色矩阵的乘法,我们可以对图像中的元素进行变换。

但是,如果需要对实时变化的图像进行实时处理,就不是每种图像变换的方式都可以用了。因为,实时变化的预览图像,会有帧率的压力,我们的处理一定要快。

上一篇中,我们已经展示了Android中,通过OpenGL展示相机预览图片的方法。

这一篇主要展示,如何在预览的图片中,加入一些简单的特效。

特效概述

QQ20180805-232214-HD.gif

这个特效的详细过程是,点击屏幕时,会在屏幕中间显示一个画中画,然后,画中画慢慢放大,逐渐透明。同时,原始预览图层的颜色不断随机变化。

代码展示

为了方便浏览,我将代码写的比较简单,完全没有考虑扩展性和封装相关的问题。而且,只展示了onDrawFrame生命周期的代码。同时,用animValue控制动画的进度。

首先,我们来看一下着色器的代码。由于特效既需要形变,也需要颜色变化,我们在gl_Position和gl_FragColor中,都引入了一个变化矩阵。

代码语言:javascript
复制
    private final String vertexShaderCode = "uniform mat4 textureTransform;\n" +
            "attribute vec2 inputTextureCoordinate;\n" +
            "attribute vec4 position;            \n" +//NDK坐标点
            "varying   vec2 textureCoordinate; \n" +//纹理坐标点变换后输出
            "uniform mat4 position_transform_matrix;\n" +
            "\n" +
            " void main() {\n" +
            "     gl_Position = position_transform_matrix * vec4(position.x,-position.y,position.z,position.w);\n" +
            "     textureCoordinate = vec2(inputTextureCoordinate.x,inputTextureCoordinate.y);\n" +
            " }";

    private final String fragmentShaderCode = "#extension GL_OES_EGL_image_external : require\n" +
            "precision mediump float;\n" +
            "uniform samplerExternalOES videoTex;\n" +
            "varying vec2 textureCoordinate;\n" +
            "uniform mat4 color_transform_matrix;\n" +
            "\n" +
            "void main() {\n" +
            "    vec4 tc = texture2D(videoTex, textureCoordinate);\n" +
            "    vec4 color = vec4(tc.r,tc.g,tc.b,tc.a); \n"+
            "    gl_FragColor = color_transform_matrix * color;\n" +
//            "    gl_FragColor = vec4(tc.r,tc.g,tc.b,1.0);\n" +
            "}";

我们通过改变position_transform_matrix,来进行形变。通过改变color_transform_matrix,来进行色彩的变换。

以下是点击事件的代码,我们会在点击后,周期性地传入一个随机的颜色矩阵,用于颜色的变换。

代码语言:javascript
复制
            mCameraGlsurfaceView.setOnClickListener {
                val timer = Timer()
                var i = 0;
                timer.schedule(object : TimerTask() {
                    override fun run() {
                        i++
                        LogUtils.d("glsurfaceview frame update i : $i")
                        glRenderer.animValue = i/20.0f;
                        glRenderer.animing = true
                        glRenderer.setFilter(object :AbsFilter {
                            override fun getColorMatrix():FloatArray{
                                var v1:Float = Math.random().toFloat()
                                var v2:Float = Math.random().toFloat()
                                var v3:Float = Math.random().toFloat()
                                return floatArrayOf(
                                        v1, 0f, 0f, 0f,
                                        0f, v2, 0f, 0f,
                                        0f, 0f, v3, 0f,
                                        0f, 0f, 0f, i/20.0f)
                            }
                        })
                        if (i == 20){
                            glRenderer.animing = false
                            glRenderer.animValue = 0.001f
                            glRenderer.setFilter(object :AbsFilter {
                                override fun getColorMatrix():FloatArray{
                                    return floatArrayOf(
                                            1f, 0f, 0f, 0f,
                                            0f, 1f, 0f, 0f,
                                            0f, 0f, 1f, 0f,
                                            0f, 0f, 0f, 1f)
                                }
                            })

                            timer.cancel()
                        }

                    }
                }, 0, 20)

            }

以下是onDrawFrame

原始预览图层的颜色变化:

代码语言:javascript
复制
   @Override
   public void onDrawFrame(GL10 gl) {
       activeProgram();

       if (mSurfaceTexture != null) {
           GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
           mSurfaceTexture.updateTexImage();
           GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, mPosCoordinate.length / 2);
       }
       renderEffect();
   }

   private FloatBuffer convertFloatBuffer (float[] vectices , int sizeByte){
       FloatBuffer floatBuffer;
       floatBuffer =  ByteBuffer.allocateDirect(
               vectices.length*sizeByte)
               .order(ByteOrder.nativeOrder()).asFloatBuffer();
       floatBuffer.put(vectices).position(0);
       return floatBuffer;
   }

   private float[] createColorTransVertices(){
       if (mEffectFilter == null){
           return new float[] {
                   1, 0, 0, 0,
                   0, 1, 0, 0,
                   0, 0, 1, 0,
                   0, 0, 0, 1
           };
       }
       return mEffectFilter.getColorMatrix();
   }
   private void activeProgram() {
       // 将程序添加到OpenGL ES环境
       GLES20.glUseProgram(mProgram);
       mSurfaceTexture.setOnFrameAvailableListener(mOnFrameAvailableListener);
       // 获取顶点着色器的位置的句柄
       uPosHandle = GLES20.glGetAttribLocation(mProgram, "position");
       aTexHandle = GLES20.glGetAttribLocation(mProgram, "inputTextureCoordinate");

       FloatBuffer mPosBuffer = convertToFloatBuffer(mPosCoordinate);
       FloatBuffer mTexBuffer;
       if(camera_status == 0){
           mTexBuffer = convertToFloatBuffer(mTexCoordinateBackRight);
       }else{
           mTexBuffer = convertToFloatBuffer(mTexCoordinateForntRight);
       }

       GLES20.glVertexAttribPointer(uPosHandle, 2, GLES20.GL_FLOAT, false, 0, mPosBuffer);
       GLES20.glVertexAttribPointer(aTexHandle, 2, GLES20.GL_FLOAT, false, 0, mTexBuffer);

       // 启用顶点位置的句柄
       GLES20.glEnableVertexAttribArray(uPosHandle);
       GLES20.glEnableVertexAttribArray(aTexHandle);

       FloatBuffer mColorTransMatrixBuffer = convertFloatBuffer(createColorTransVertices() , 4);
       GLES20.glUniformMatrix4fv(mColorTransMatrixHandler , 1  , false , mColorTransMatrixBuffer);
       GLES20.glEnableVertexAttribArray(mColorTransMatrixHandler);
       float[] posTrans = new float[] {
               1, 0, 0, 0,
               0, 1, 0, 0,
               0, 0, 1, 0,
               0, 0, 0, 1
       };
       FloatBuffer mPosTransMatrixBuffer = convertFloatBuffer(posTrans , 4);
       GLES20.glUniformMatrix4fv(mPosTransMatrixHandler , 1  , false , mPosTransMatrixBuffer);
       GLES20.glEnableVertexAttribArray(mPosTransMatrixHandler);
   }

可以看到,在原始画面的渲染中,我们的pos转换矩阵,使用了单位矩阵。而颜色转换矩阵,则使用了mEffectFilter.getColorMatrix(),即外部传入的颜色矩阵,进行随机颜色变换。


接下来,是画中画特效的渲染过程:

代码语言:javascript
复制
    private void renderEffect(){
        // --- for click effect

        FloatBuffer mPosBuffer = convertToFloatBuffer(mPosCoordinate);
        FloatBuffer mTexBuffer;
        if(camera_status == 0){
            mTexBuffer = convertToFloatBuffer(mTexCoordinateBackRight);
        }else{
            mTexBuffer = convertToFloatBuffer(mTexCoordinateForntRight);
        }

        GLES20.glVertexAttribPointer(uPosHandle, 2, GLES20.GL_FLOAT, false, 0, mPosBuffer);
        GLES20.glVertexAttribPointer(aTexHandle, 2, GLES20.GL_FLOAT, false, 0, mTexBuffer);

        // 启用顶点位置的句柄
        GLES20.glEnableVertexAttribArray(uPosHandle);
        GLES20.glEnableVertexAttribArray(aTexHandle);
        float[] colorTrans = new float[] {
                1.0f, 0, 0, 0,
                0, 1.0f, 0, 0,
                0, 0, 1.0f, 0,
                0, 0, 0, 0.5f*(1- animValue /1.0f)
        };
        // 半透明显示,需要开启
        GLES20.glEnable(GLES20.GL_BLEND);
        GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);

        FloatBuffer mColorTransMatrixBuffer = convertFloatBuffer(colorTrans , 4);
        GLES20.glUniformMatrix4fv(mColorTransMatrixHandler , 1  , false , mColorTransMatrixBuffer);
        GLES20.glEnableVertexAttribArray(mColorTransMatrixHandler);
        float[] posTrans = new float[] {
                1.0f, 0, 0, 0,
                0, 1.0f, 0, 0,
                0, 0, 1, 0,
                0, 0, 0, 1
        };
        if (animing){
            posTrans = new float[] {
                    0.5f+ animValue, 0, 0, 0,
                    0, 0.5f+ animValue, 0, 0,
                    0, 0, 1, 0,
                    0, 0, 0, 1
            };
        }
        FloatBuffer mPosTransMatrixBuffer = convertFloatBuffer(posTrans , 4);
        GLES20.glUniformMatrix4fv(mPosTransMatrixHandler , 1  , false , mPosTransMatrixBuffer);
        GLES20.glEnableVertexAttribArray(mPosTransMatrixHandler);

        if (mSurfaceTexture != null) {
            mSurfaceTexture.updateTexImage();
            GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, mPosCoordinate.length / 2);
        }
    }

与activeProgram方法的流程类似,唯一不同的只是位置矩阵和颜色矩阵。在画中画的特效中,颜色矩阵接近于一个单位矩阵,只是透明度会渐渐变小。而顶点坐标的矩阵,则会随着特效动画的进程不断变化。xy值不对增大。


以上就是一个简单的基于OpenGL的动画特效。OpenGL动效的关键在于根据着色器的代码,插入需要变换的变量。如顶点变换矩阵和颜色变换矩阵,然后根据时间或其他参数,对矩阵进行变换,从而达到改变渲染的目的。

如有问题,欢迎指正。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018.08.05 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 特效概述
  • 代码展示
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档