Android OpenGLES滤镜开发之仿抖音灵魂出窍

前言

前几篇写的滤镜效果比如美颜、大眼、贴纸效果都是在录制视频之前,这个灵魂出窍的效果是在录制视频之后,可以对视频添加效果。

思路

可以观察到灵魂出窍的效果,其实其主图像本没有什么变化,只是新增了一张进行缩放的纹理,跟主图像的alpha进行线性融合的。
怎么去取灵魂呢,灵魂是跟着视频所播放的内容不断更新的,不可能一直只是同一个图像。所以这里的思路是每X帧拷贝一帧作为灵魂,然后将灵魂按比例放大,最后将灵魂与主图像进行混合。

实现
片元着色器

因为视频录制出来的是YUV格式的数据,但是在OpenGL中是需要的RGB颜色,所以需要将YUV格式的数据转化成RGB格式的,这里需要将YUV格式的数据进行分离之后,再转换。转换是有公式的,用公式进行计算就好啦。

uniform sampler2D sampler_y; //yuv
uniform sampler2D sampler_u;
uniform sampler2D sampler_v;

//透明度
uniform float alpha;

void main(){
    //4个float数据 y、u、v保存在向量中的第一个
    // 0.5是128数据归一后的
    float y = texture2D(sampler_y,aCoord).r;
    float u = texture2D(sampler_u,aCoord).r - 0.5;
    float v = texture2D(sampler_v,aCoord).r - 0.5;
    // yuv转rgb的公式
    //R = Y + 1.402 (v-128)
    //G = Y - 0.34414 (u - 128) - 0.71414 (v-128)
    //B = Y + 1.772 (u- 128)
    vec3 rgb;
    //u - 128
    //1、glsl中 不能直接将int与float进行计算
    //2、rgba取值都是:0-1 (128是0-255 归一化为0-1 128就是0.5)
    rgb.r = y + 1.402 * v;
    rgb.g = y - 0.34414 * u - 0.71414* v;
    rgb.b = y + 1.772 * u;
    //rgba
    gl_FragColor = vec4(rgb,alpha);
渲染主图像

首先就是获取索引,然后创建Y、U、V三个纹理,分别进行传值,然后先画主图像,不进行任何的缩放平移。

        //分离yuv数据,然后传值
        bodyImage.initData(yuv);
        if(!bodyImage.hasImage()){
            return;
        }
        GLES20.glUseProgram(mGLProgramId);

        //不进行任何的缩放平移
        Matrix.setIdentityM(matrix,0);
        GLES20.glUniformMatrix4fv(vMatrix,1,false,matrix,0);
        //主图像不透明
        GLES20.glUniform1f(mAlpha,1);
        //传值
        onDrawBody(bodyImage);

上面代码就是首先将主图像的YUV数据进行分离,分离YUV就不介绍了,比较简单,然后设置setIdentityM设置一个单位矩阵,这个矩阵是没有任何缩放平移效果的。这个方法的源码是

    /** 最终得出的矩阵
    * 1 0 0 0
    * 0 1 0 0
    * 0 0 1 0
    * 0 0 0 1
    */
 public static void setIdentityM(float[] sm, int smOffset) {
        for (int i=0 ; i<16 ; i++) {
            sm[smOffset + i] = 0;
        }
        for(int i = 0; i < 16; i += 5) {
            sm[smOffset + i] = 1.0f;
        }
    }

这个矩阵需要个传入的4个顶点坐标进行运算,为什么这样的单位矩阵就是没有任何效果的呢,可以来进行矩阵运算一下,输入顶点坐标为(1,1,0,0)的话,与矩阵运算之后的结果还是(1,1,0,0)

设置完需要的矩阵,YUV三个纹理,就需要将这些顶点,YUV纹理传递给着色器就可以了。这个里就看一下Y数据的传递,UV数据传递也是一样的。

      //传递yuv数据
       //激活纹理
        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
        //绑定mTextures[0]纹理
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D,mTextures[0]);
    //  glTexImage2D方法是指定一个二维纹理图像 这里就是将Y数据与mTextures[0]这个纹理相绑定
    //第一个参数target 是指定纹理单元的目标纹理,二维就是GLES20.GL_TEXTURE_2D是有取值范围的
    //第二个参数是level,是指定详细编号,0表示基本图像级别,n就是第n个缩略图
    //第三个参数internalformat ,指定纹理的内部格式,必须是下列符号常量之一:GL_ALPHA,GL_LUMINANCE,GL_LUMINANCE_ALPHA,GL_RGB,GL_RGBA。因为我们这次传递的是YUV格式数据,所以需要给GL_LUMINANCE,意思是量度模型
    //第四个参数width,纹理图像的宽,第五个就是纹理图像的高,第六个就是边框的宽度border必须为0
    //第七个参数format,指定纹理数据的格式,和internalformat是相匹配的
    //第八个参数type,是指纹理数据的类型,这里给的是无符号的byte类型
    //第九个参数java.nio.Buffer pixels,像素值,这个给的是Y数据,如果是RGA格式的,一般就是0像素
   GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D,0,GLES20.GL_LUMINANCE,
                mOutputWidth,mOutputHeight,0,GLES20.GL_LUMINANCE,
                GLES20.GL_UNSIGNED_BYTE,bodyImage.getY());
        GLES20.glUniform1i(mSamplerY,0);
渲染灵魂

因为灵魂出窍这个效果,灵魂是不断的更新放大,所以我们这里是采用了间隔X帧拷贝一帧作为灵魂。

       interval++ ;
        //没有保存灵魂或者 使用次数已经达到上限了
        // 灵魂只能使用X次,因为需要不断的更新灵魂
        if (!soulImage.hasImage() || interval > mFps){
            interval = 1;
            // 记录新灵魂
            soulImage.initData(yuv);
        }

        if (!soulImage.hasImage()){
            return;
        }

        //画灵魂
        //需要和主图像进行混合的,所以需要开启混合模式
        GLES20.glEnable(GLES20.GL_BLEND);
        //这两个参数之前也说过了,这次的源是灵魂,目标是主图像
        //这次是主图像不变,也就是目标不变
        GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA,GLES20.GL_ONE);

        //设置matrix为单位矩阵
        Matrix.setIdentityM(matrix,0);
        //设置缩放大小
        //本次放大为 1+ 当前灵魂次数占总次数 * 2的比例
        //这个是自己设置的慢慢放大的效果
        float scale = 1.0f + interval/(mFps * 2.f);
        //矩阵的缩放函数,结果保存在martix中
        Matrix.scaleM(matrix,0,scale,scale,0);
        GLES20.glUniformMatrix4fv(vMatrix,1,false,matrix,0);
        GLES20.glUniform1f(mAlpha,0.1f + (mFps -interval) / 100.f);
吐槽

这样就差不多可以实现了灵魂出窍的效果了,我本来是使用MediaCodec编解码来进行录制和播放的,但是录制没问题,播放的时候就有问题了,MediaCodec的兼容性真滴太差了,它在个别机型上面播放的时候,可能兼容不了,就会播放出黑白视频,木有颜色~~所以这里就不放效果图了,改天我用FFmeg来解码播放吧,还是FFmeg比较好用些。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: OpenGL ES是一种用于开发图形应用程序的开放式图形编程接口。它被广泛用于Android系统上的游戏开发以及其他需要高性能图形渲染的应用程序中。下面是关于OpenGL ESAndroid开发中的一些要点: 1. OpenGL ES的版本:Android支持多个不同版本的OpenGL ES,如OpenGL ES 1.0、1.1、2.0和3.0。开发者根据自己的需求选择合适的版本。 2. 渲染管线:OpenGL ES使用可编程的渲染管线来处理图形渲染。开发者可以通过创建顶点着色器和片段着色器程序来自定义渲染过程,从而实现各种效果。 3. 缓冲对象:开发者可以使用OpenGL ES来创建和管理缓冲对象,如顶点缓冲对象(VBO)和帧缓冲对象(FBO)。这些缓冲对象用于存储图形数据,提高绘制效率。 4. 纹理映射:OpenGL ES允许开发者将纹理映射到三维对象上,以实现更加逼真的图形效果。开发者可以通过加载纹理图像并将其映射到顶点上来创建细节丰富的模型。 5. 事件处理:OpenGL ES提供了一些函数来处理触摸事件、加速度计变化等输入信息。开发者可以使用这些函数来实现交互式的图形应用程序。 6. OpenGL ES的集成:在Android开发中,开发者可以通过GLSurfaceView类来集成OpenGL ES。GLSurfaceView是Android提供的一个用于显示OpenGL ES图形的视图类。 总结来说,OpenGL ESAndroid开发中用于实现高性能图形渲染的重要工具。开发者可以利用它来创建各种各样的游戏和图形应用程序,并通过自定义着色器和纹理映射等技术来增加细节和逼真度。 ### 回答2: OpenGLES是一种用于开发移动设备和嵌入式系统的图形渲染API,它主要用于在Android平台上开发游戏和图形应用程序。使用OpenGLES开发者可以利用硬件加速的图形渲染功能,提供流畅的图形效果和高性能的图形渲染。 在Android平台上进行OpenGLES开发,首先需要在应用程序中引入OpenGLES库文件,并进行相关的环境设置。然后,开发者可以使用OpenGLES API提供的各种函数和方法来创建图形对象、设置渲染状态、进行变换和纹理映射等操作。同时,还可以使用OpenGLES提供的着色器语言,自定义渲染管线,实现更高级的图形效果。 在开发过程中,需要注意OpenGLES使用的坐标系统是以屏幕为中心的坐标系,而不是传统的以左上角为原点的坐标系。因此,在创建图形对象时,需要进行坐标转换。此外,还需要注意管理资源和内存,避免内存泄漏和资源浪费。 在实际开发中,可以利用OpenGLES创建各种图形效果,如平面、立体、光照、阴影等。同时,还可以通过OpenGLES实现用户交互,如触摸屏幕,操作物体的变换等。此外,还可以使用OpenGLES与其他Android组件进行交互,如利用OpenGL ES绘制图像进行相机预览、视频播放等。 总之,OpenGLESAndroid开发中具有重要的作用,能够实现高性能的图形渲染和丰富的图形效果,为开发者提供了强大的工具和技术支持。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值