关于android:Android-实现抖音传送带特效

42次阅读

共计 4425 个字符,预计需要花费 12 分钟才能阅读完成。

一、实现成果

1.1 首先来看抖音的传送带特效

从上图能够看到,抖音的传送带特效有如下特点

  • 屏幕左半边局部是失常预览视频
  • 屏幕右半边局部像传送带个别,将画面一直地像左边 运送

依据此特效的特点,咱们能够制作出各种乏味的视频

1.2 笔者实现传送带特效

从上图来看,笔者实现的成果基本上和抖音实现的统一

那么,对于该特效,咱们应该如何去实现呢?

其实在介绍抖音蓝线挑战特效那一章曾经将到一个外围知识点Fbo,对,没错,过后做蓝线挑战特效用到的就是Fbo,接下来传送带特效也须要应用 Fbo 的保留上一帧性能

接下来,咱们就来进行特效剖析和具体实现

二、特效剖析

首先,依据下面的效果图,咱们能够简略画出示意图,如下图所示(小格子的数量越多,画面越精密)

咱们以横向进行剖析

OpenGLES 中,纹理坐标程度方向的起始地位在左方(精确的说是在左上角,这里只是剖析横向的成果,故图上标点 0.0 随便标在左方,便于剖析)依据下面的效果图,理解到,该特效有两个特点

  • 屏幕左半边局部是失常预览视频
  • 屏幕右半边局部像传送带个别,将画面一直地像左边运送

这里,我用了运送一词,那么,咱们得首先晓得,它运送的是什么

2.1 运送什么?

通过剖析特效图,咱们晓得,图像右半局部是一直地向左边挪动,而左半局部是失常预览的,看起来就如同是从左半局部的边缘处一直挪动到左边,那么从这里能够得出一个小论断

它运送的是左半局部的边缘区域,依据上图,精确的说是中线右边 0 区域的画面

那么,晓得了这点,咱们就高深莫测了

2.2 它是如何运送的?

后面,咱们晓得了它运送的是 0 区域的画面,那么接下来就来剖析下,它是如何运送的

  • 在预览时,相机画面个别都是失常显示,0 区域的画面当然也是失常一帧帧刷新
  • 当 0 区域显示第一帧(简称 f1,前面以 f 开后,数字为帧序)时,将其挪动到 1 区域
  • 当 0 区域显示 f2 时,将 1 区域的 f1 挪动到 2 区域,将 0 区域的 f2 挪动到 1 区域
  • 顺次类推,就能够将 0 区域的画面源源不断地运送到左边
2.3 Fbo

其实,在晓得了它是运送什么,且如何运送后,咱们还是无奈得悉如何实现这一特效

此刻,就该 Fbo 退场了,后面蓝线挑战特效的篇章曾经对其做了详细描述,当初简略介绍下

  • 能够将 Oes 纹理转换成 2D 纹理
  • 能够将纹理数据不显示在屏幕上,并保留下来

这里,咱们要实现该特效,就要应用它的保留帧数据的性能

2.4 特效实现

在下面,咱们曾经晓得了该特效是如何运送数据,那么通过下图,咱们来理解如何应用 Fbo 实现

从下面的剖析可知,该特效运送的是左半局部的边缘区域,所有有如何下实现步骤:

  • 首先假如每个小格的步长为 0.1,那么左半局部的边缘区域就是0.4 ~ 0.5 这个区域
  • Fbo 能够保留上一帧,那么在渲染时,咱们将上一帧的数据保留下来
  • 在渲染的时候,会有两个纹理,一个是相机的失常预览纹理,另一个是保留的上一帧,此时,咱们在着色器里就要进行判断
  • 当纹理坐标 x 小于 0.5 时,显示相机的失常预览画面
  • 当纹理坐标 x 大于 0.5 时,显示保留的上一帧画面,不过这里要留神,并不是对应坐标的上一帧数据,即,不是 0.5 ~ 1.0 区域的数据,而是 0.4 ~ 0.9 区域的数据,大家能够思考下这是为什么,前面具体实现的时候会有解答

这样,当相机一直产生预览数据时,右半局部将一直地将左半局部的边缘区域向左边运送

三、具体实现

后面咱们剖析了该特效的整个实现流程,接下来就是具体的实现

首先,先上大家最关怀的着色器代码

3.1 着色器

顶点着色器

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

对于顶点着色器,并没有做任何非凡解决 片元着色器

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

对于片元着色器,要害就在于 main() 函数外面的 if 判断,后面也有提到,会对纹理坐标进行一个判断

  • 当 x 小于 0.5 时,显示相机预览画面
  • 当 x 大于 0.5 时,显示上一帧的数据,且取的是对应坐标往左偏移的数据(uOffset是偏移量,能够了解成小格子的宽度)

那么对于为什么要偏移呢?

这是因为通过下面,咱们能够晓得,该特效是从左半局部的边缘区域开始运送的,那么如果咱们从对应坐标取,那么不就得不到左半局部区域的坐标了吗,所有得偏移一个小格子的宽度,从而失去对应的数据 这样,每帧渲染时,都取 0.4 ~ 0.9 区域数据显示到 0.5 ~ 1.0 区域,从而就实现了该传送带特效

在晓得了如何实现该特效后,咱们还能够实现纵向的传送带特效,只须要将片元着色器里的 x 改为 y 即可

precision mediump float;
uniform sampler2D uSampler;
uniform sampler2D uSampler2;
varying vec2 vCoordinate;
uniform float uOffset;
void main(){if (vCoordinate.y < 0.5) {gl_FragColor = texture2D(uSampler, vCoordinate);
    } else {gl_FragColor = texture2D(uSampler2, vCoordinate - vec2(0.0, uOffset));
    }
}
3.2 Java 代码实现局部

上面是 Java 代码实现局部

这外面应用了一个 lastRender 保留上一帧数据,从而在下一次渲染时可能应用

public class ConveyorBeltHFilter extends BaseFilter {
    private final BaseRender lastRender;

    private int uSampler2Location;
    private int uOffsetLocation;

    private int lastTextureId = -1;

    private float offset = 0.01f;

    public ConveyorBeltHFilter(Context context) {
        super(
                context,
                "render/filter/conveyor_belt_h/vertex.frag",
                "render/filter/conveyor_belt_h/frag.frag"
        );

        lastRender = new BaseRender(context);

        lastRender.setBindFbo(true);
    }

    @Override
    public void onCreate() {super.onCreate();
        lastRender.onCreate();}

    @Override
    public void onChange(int width, int height) {super.onChange(width, height);
        lastRender.onChange(width, height);
    }

    @Override
    public void onDraw(int textureId) {super.onDraw(textureId);
        lastRender.onDraw(getFboTextureId());
        lastTextureId = lastRender.getFboTextureId();}

    @Override
    public void onInitLocation() {super.onInitLocation();
        uSampler2Location = GLES20.glGetUniformLocation(getProgram(), "uSampler2");
        uOffsetLocation = GLES20.glGetUniformLocation(getProgram(), "uOffset");
    }

    @Override
    public void onActiveTexture(int textureId) {super.onActiveTexture(textureId);
        GLES20.glActiveTexture(GLES20.GL_TEXTURE1);
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, lastTextureId);
        GLES20.glUniform1i(uSampler2Location, 1);
    }

    @Override
    public void onSetOtherData() {super.onSetOtherData();
        GLES20.glUniform1f(uOffsetLocation, offset);
    }
}

以上就是抖音传送带特效的实现全过程,心愿大家喜爱!!!

四、GitHub

github 地址:https://github.com/JYangkai/M…

  • ConveyorBeltHFilter.java
  • ConveyorBeltVFilter.java

原文链接:https://juejin.cn/post/699849…

文末

您的点赞珍藏就是对我最大的激励!
欢送关注我,分享 Android 干货,交换 Android 技术。
对文章有何见解,或者有何技术问题,欢送在评论区一起留言探讨!

正文完
 0