Android 音视频开发【特效篇】【二】简单实用有趣的特效 | 8月更文挑战

1,473 阅读4分钟

这是我参与8月更文挑战的第7天,活动详情查看:8月更文挑战

前面我们介绍了一些很酷炫的特效,比如蓝线挑战传送带特效,不过对于初学者来说,难度较大,故,本章将介绍一些简单但有趣的特效

一、Shader特效

对于OpenGLES的开发来说,最难的部分还是着色器,无论是顶点着色器,还是片元着色器,都是OpenGLES开发的重点,当然,这两者中,片元着色器的使用几率更高点

为什么要对着色器进行处理?

因为做特效,最重要的就是对其像素进行一系列操作,从而完成想要的效果,比如,

我想让一张图片灰度化,那么就得操作其像素

我想让一张图片分屏,那么也得操作其像素

。。。

特效分为几种

  • 禁止的,具有固定的形态
  • 动态的,会随着时间的流逝而运动

前面我们介绍的蓝线挑战传送带特效,都属于动态的

下面,我们将介绍几款静态的特效

二、特效分析和展示

下面所展示的特效,基本上只修改了片元着色器顶点着色器固定的,如下所示:

顶点着色器

attribute vec4 aPos;
attribute vec2 aCoordinate;
varying vec2 vCoordinate;
void main(){
    vCoordinate = aCoordinate;
    gl_Position = aPos;
}

2.1. 灰度图

让画面变灰以下有几种方法:

  • R、G、B三者的平均值
  • R、G、B任一值当做共同值
  • R、G、B做加权平均当做共同值

三种方法都可以使图片变灰,这里我采用了第三种

precision mediump float;
uniform sampler2D uSampler;
varying vec2 vCoordinate;
const vec3 GRAY = vec3(0.299, 0.587, 0.144);
void main(){
    vec4 sourceColor = texture2D(uSampler, vCoordinate);
​
    float gray =dot(sourceColor.rgb, GRAY);
​
    gl_FragColor = vec4(gray, gray, gray, sourceColor.a);
}

注意到,在片元着色器中,使用当前像素值与vec3(0.299, 0.587, 0.144)进行向量的点积操作,即

gray=r0.299+g0.587+b0.144gray = r * 0.299 + g * 0.587 + b * 0.144

效果如下:

灰度图.png

2.2 两分屏

对于分屏特效,我只需要对其纹理坐标进行处理即可

两分屏,则是将屏幕分为两半,且两半显示的内容的一半,那么这两半屏幕的内容显示的是哪个区域内?

所以,我们只需要搞懂显示的区域即可得到对应的效果

当然,你可以随意显示想要显示的区域,不过有一点需要注意,就是一半屏幕的宽或者高是0 ~ 0.5,所有你能显示的区域的范围只能是0.5,超过或者小于,则会造成画面拉伸或压缩 下面的片元着色器显示的是0.25 ~ 0.75之间的区域

precision mediump float;
uniform sampler2D uSampler;
varying vec2 vCoordinate;
void main(){
    if (vCoordinate.y < 0.5) {
        gl_FragColor = texture2D(uSampler, vCoordinate + vec2(0.0, 0.25));
    } else {
        gl_FragColor = texture2D(uSampler, vCoordinate - vec2(0.0, 0.25));
    }
}

效果如下:

两分屏.png

2.3 三分屏

对于两分屏的原理搞懂了,那么三分屏自然不在话下

precision mediump float;
uniform sampler2D uSampler;
varying vec2 vCoordinate;
void main(){
    if (vCoordinate.y < 0.33) {
        gl_FragColor = texture2D(uSampler, vCoordinate + vec2(0.0, 0.33));
    } else if (vCoordinate.y > 0.66){
        gl_FragColor = texture2D(uSampler, vCoordinate - vec2(0.0, 0.33));
    } else {
        gl_FragColor = texture2D(uSampler, vCoordinate);
    }
}

效果如下:

三分屏.png

2.4 四分屏

片元着色器

precision mediump float;
uniform sampler2D uSampler;
varying vec2 vCoordinate;
void main(){
    float x = vCoordinate.x;
    float y = vCoordinate.y;
​
    if (x < 0.5) {
        x *= 2.0;
    } else {
        x = (x - 0.5) * 2.0;
    }
​
    if (y < 0.5) {
        y *= 2.0;
    } else {
        y = (y - 0.5) * 2.0;
    }
​
    gl_FragColor = texture2D(uSampler, vec2(x, y));
}

效果如下:

四分屏.png

2.5 九分屏

片元着色器

precision mediump float;
uniform sampler2D uSampler;
varying vec2 vCoordinate;
void main(){
    float x = vCoordinate.x;
    float y = vCoordinate.y;
​
    if (x < 0.33) {
        x *= 3.0;
    } else if (x < 0.66) {
        x = (x - 0.33) * 3.0;
    } else {
        x = (x - 0.66) * 3.0;
    }
​
    if (y < 0.33) {
        y *= 3.0;
    } else if (y < 0.66) {
        y = (y - 0.33) * 3.0;
    } else {
        y = (y - 0.66) * 3.0;
    }
​
    gl_FragColor = texture2D(uSampler, vec2(x, y));
}

效果如下:

九分屏.png

对于分屏的逻辑,可能存在效率问题,因为用到了比较多的if-else,不过这里就当是参考,实际使用时可以考虑优化这部分的逻辑

2.6 画中画

我们在之前的高斯模糊与毛玻璃那一章介绍了高斯模糊的原理,并讲解了它的一个使用特效毛玻璃,那么接下来我们将使用毛玻璃的效果制作一个画中画的特效

首先实现的效果:

画中画.png

可以看到:

  • 画面中心是清晰的图像
  • 上下两个部分是毛玻璃效果、且放大了的图像

那么就可以知道,画中画特效其实是有两张纹理组成,一张是原图像,另一张是放大且添加毛玻璃效果的图像

画中画=原图像+放大毛玻璃图像画中画 = 原图像 + 放大*毛玻璃图像

具体的代码比较多,且涉及到Fbo,这里就不贴出来了

感兴趣的可以到GitHub上查看

三、GitHub

灰度图 GrayFilter.java

两分屏 TwoPartFilter.java

三分屏 ThreePartFilter.java

四分屏 FourPartFilter.java

九分屏 NinePartFilter.java

画中画 PipFilter.java