Android 实现抖音传送带特效

Android仿抖音主页效果实现 “抖音”都玩过吧,是不是很好玩,我反正是天天刷,作为一个非著名的Android低级攻城狮,虽然技术菜的一匹,但是也经常刷着刷着会思考:咦?这玩意是用哪个控件做的?这个效果是咋实现的啊?由于本人技术水平有限,所以今天咱就先挑个比较简单的来看看是如何实现的,思考再三,我们就拿抖音首页的这个效果来练练手吧,话不多说,开搞! 阅读详情

一、实现效果

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/MediaDemo/

  • ConveyorBeltHFilter.java
  • ConveyorBeltVFilter.java

原文链接:https://juejin.cn/post/6998491350018555941

文末

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

OpenGL ES 实现抖音传送带特效 Android 相机出图是 YUV 格式的,这里为了拷贝处理方便,先使用 OpenCV 将 YUV 图像转换为 RGBA 格式,当然为了追求性能直接使用 YUV 格式的图像问题也不大。 阅读详情

相关推荐

OpenGL ES 实现抖音“蓝线挑战”特效

前面文章中我们实现了抖音传送带特效,发现大家普遍对抖音视频特效感兴趣,那么本文将继续这个抖音特效实现系列,后面也将带来更多的关于抖音视频特效分析和实现的文章。本文带给大家的是抖音的经典视频特效“蓝线挑战”实现,为啥选择“蓝线挑战”?之前这个特效没太在意,知道看到老罗直播团队用这个特效拍了一个短视频,发现挺有意思的。之前“蓝线挑战”特效有人实现过它的变种,但是看起来搞的有点复杂了,本文的实现思路就非常简单,抖音“蓝线挑战”和“传送带”实现原理基本相同:每次更新预览帧图像的特定区域进行渲染。抖音“蓝线挑战”特效

字节流动 3万+

android实现抖音直播间飘屏送礼物动画(超简单)

android实现抖音直播间飘屏动画(超简单)

小口口的博客 4453

Android 横向纵向传送带效果.zip

Android 横向纵向传送带效果让你的RecyclerView秀出传送带效果,支持横向和纵向两种选择。

android仿抖音效果

https://blog.youkuaiyun.com/JArchie520/article/details/110791624

我是一只小菜鸟,飞丫飞丫飞~~~ 2793

Android 仿写抖音视频切换功能.rar

Android 仿写抖音视频APP端清淅度切换功能,初始化播放器(只需调用一次即可,建议在application中初始化),在本例子中,滑屏时SurfaceView出现白屏和黑屏问题得到了解决,准备成功之后可以调用start方法开始播放,播放正常完成时触发,视频清晰度切换成功后触发,视频清晰度切换失败时触发。

3d传送带效果

3D传送带效果的实现。 欢迎大家使用。是个小例子

抖音传送带特效实战

/今日科技快讯/近日,vivo内部通知将取消大小周,改为每周5天工作制。以下为vivo方面的内部群通知:经管理委员会研究决定,公司计划取消大小周工作制,改为每周5天工作制。各团...

郭霖 403

Android仿抖音列表效果

主要为大家详细介绍了Android仿抖音列表效果,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

Android传送带动画,抖音定格传送带特效拍摄

抖音定格传送带特效拍摄是非常有趣的特效软件,用户可以在这上面尝试各种好玩的特效视频拍摄哦,而且这上面还有非常多的拍摄技巧,可以和好友一起在这上面拍摄并分享出去哦,喜欢的伙伴们就可以下载试试的哦。抖音定格传送带特效拍摄app特点1、所有的特效素材都是可以免费使用的哦,用户可以放心的在这上面拍摄照片或者视频的呢;2、每次用户拍摄完的视频都是可以直接在抖音上直接进行分享的哦,这样就会有更多的人看到呢;3...

weixin_29355377的博客 703

Android仿抖音APP

国庆放假,自己撸的App 模仿抖音写的App,实现了类似抖音的部分功能。即使聊天部分还没有写完,抽空写完,开源 功能简单,为提高代码的复用性,采用封装— 效果图 开发环境    开发工具 Android studio 3.4 架构  mvp 技术点 retrofit2+rxjava(网络请求) butterknif appintro(启动页) PickerView phoenix(视屏处理) glide 自定义View 等等 基本功能图 包结构 后台 作者:一抹离愁

Android直播app送礼物连击动画效果(实例代码)

最近在做公司的直播项目,需要实现一个观看端连击送礼物的控件,下面给大家分享实例代码,需要的的朋友参考下吧

小视频app源码Android OpenGL ES 实现传送带特效

小视频app源码Android OpenGL ES 实现传送带特效的相关代码 Android 相机出图是 YUV 格式的,这里为了拷贝处理方便,先使用 OpenCV 将 YUV 图像转换为 RGBA 格式,当然为了追求性能直接使用 YUV 格式的图像问题也不大。 cv::Mat mati420 = cv::Mat(pImage->height * 3 / 2, pImage->width, CV_8UC1, pImage->ppPlane[0]); cv::Mat matRgba = cv

云豹网络科技 181

STM32 HAL库状态机实现按键多事件检测(无阻塞高效处理)

本文详细介绍基于STM32 HAL库的状态机实现按键多事件检测方法,包括单击、双击等高效无阻塞处理。通过定时器中断和GPIO中断优化,结合状态机设计,显著提升系统响应效率和实时性,适用于嵌入式系统开发。

tech5的博客 1016

Android OpenGL ES 3.0 开发完全指南:从零基础到高级特效实战

想要掌握 Android 平台上的高性能图形编程吗?NDK_OpenGLES_3_0 项目为你提供了一个系统性的 Android OpenGL ES 3.0 学习教程,从基础三角形绘制到复杂的 3D 模型渲染,再到抖音特效实现,这个开源项目涵盖了 OpenGL ES 3.0 开发的方方面面。无论你是 Android 开发者想提升图形处理能力,还是想学习移动端图形编程,这个项目都是绝佳的学习资源。

gitblog_01047的博客 626

无迹卡尔曼滤波(UKF)推导

提出,使用了一种不同的方法来计算误差协方差矩阵。它不使用(连续时间)黎卡提方程(Riccati Equations,形如。的一类非线性方程)或(离散时间)协方差传播和更新规律。关键的思想是基于一组特殊的样本点来估计误差协方差,这些点被称为。无迹卡尔曼滤波(Unscented Kalman Filter),最初由。点(Sigma Points),这些点直接通过原始非线性模型传播。无迹变换表示有三个特殊样本点的高斯分布,称为。

生活如此美好,致敬每一个热爱“热爱”的人! 1778

跟着团子学SAP PS:项目计划成本与项目预算设计思路

​在做SAP PS模块概览培训的时候,很多时候都会用到下面的一页PPT片子讲到项目计划成本与预算的关系,但真正这幅片子想表达的具体内容甚至大部分PS顾问也是无法阐明清楚的。 而SAP PS的设计其实是在丰富的项目管理学的基础上而开展的,所以今天从业务上结合SAP中的功能点讲一下项目成本计划及预算的逻辑。 在项目管理的十大知识领域中,项目成本管理主要是包括为使项目在批准的预算内完成而对成本进行规划...

Lucas1211的博客 3万+

ExoPlayer视频滤镜开发指南:5个手法轻松实现实时视频特效渲染

想给 Android 播放器加上一套专业的视频滤镜能力?ExoPlayer视频滤镜方案,是你在不重写渲染引擎的前提下最快见效的路径。本文用 5 个实战手法,带你从零掌握基于 OpenGL 的实时特效渲染,读完即可动手集成到自己的项目里。 ## 为什么视频App需要自定义滤镜:从播放器到画面加工厂的进化 先讲一个常见的场景:你做了一个短视频或直播类 App,产品经理隔三差五提需求——"加个复古滤

gitblog_01157的博客 1090

Android Qcom Display学习(十一)

该系列文章总目录链接与各部分简介:本章主要是基于MIPI Payload传输接口的学习,包括Generic和DCS的接口不同。底层都是基于去封装的,下面第一部分调用原生接口,第二部分分析高通是如何进行读写操作的。

Android驱动开发 3184
上一篇: Android实现优雅快速的网络请求
下一篇: 效果炸了··Android自定义View实现一个炫酷的时钟
不秃头de程序猿
博客等级 码龄5年 4090粉丝 1478原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值