Android OpenGL ES 实现抖音传送带特效(原理解析)

 更新时间:2022年07月21日 09:48:11   作者:字节流动  
这篇文章主要介绍了Android OpenGL ES 实现抖音传送带特效,抖音传送带特效推出已经很长一段时间了,前面也实现了下,最近把它整理出来了,如果你有仔细观测传送带特效,就会发现它的实现原理其实很简单,需要的朋友可以参考下

抖音 APP 真是个好东西,不过也容易上瘾,老实说你的抖音是不是反复卸载又反复安装了,后来我也发现我的几个 leader 都不刷抖音,这令我挺吃惊的。

我刷抖音主要是为了看新闻,听一些大 V 讲历史,研究抖音的一些算法特效,最重要的是抖音提供了一个年轻人的视角去观察世界。另外,自己感兴趣的内容看多了,反而训练抖音推送更多类似的优质内容,大家可以反向利用抖音的这一特点。

至于我的 leader 老是强调刷抖音不好,对此我并不完全认同。

抖音传送带特效原理

抖音传送带特效推出已经很长一段时间了,前面也实现了下,最近把它整理出来了,如果你有仔细观测传送带特效,就会发现它的实现原理其实很简单。

通过仔细观察抖音的传送带特效,你可以发现左侧是不停地更新预览画面,右侧看起来就是一小格一小格的竖条状图像区域不断地向右移动,一直移动到右侧边界位置。

预览的时候每次拷贝一小块预览区域的图像送到传送带,这就形成了源源不断地向右传送的效果。

原理图进行了简化处理, 实际上右侧的竖条图像更多,效果会更流畅,每来一帧预览图像,首先拷贝更新左侧预览画面,然后从最右侧的竖条图像区域开始拷贝图像(想一想为什么?)。

例如将区域 2 的像素拷贝到区域 3 ,然后将区域 1 的像素拷贝到区域 2,以此类推,最后将来源区域的像素拷贝到区域 0 。

这样就形成了不断传送的效果,最后将拷贝好的图像更新到纹理,利用 OpenGL 渲染到屏幕上。

抖音传送带特效实现

上节原理分析时,将图像区域从左侧到右侧拷贝并不高效,可能会导致一些性能问题,好在 Android 相机出图都是横向的(旋转了 90 或 270 度),这样图像区域上下拷贝效率高了很多,最后渲染的时候再将图像旋转回来。

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::Mat(m_SrcImage.height, m_SrcImage.width, CV_8UC4, m_SrcImage.ppPlane[0]);
cv::cvtColor(mati420, matRgba, CV_YUV2RGBA_I420);

用到的着色器程序就是简单的贴图:

#version 300 es
layout(location = 0) in vec4 a_position;
layout(location = 1) in vec2 a_texCoord;
uniform mat4 u_MVPMatrix;
out vec2 v_texCoord;
void main()
{
gl_Position = u_MVPMatrix * a_position;
v_texCoord = a_texCoord;
}

#version 300 es
precision mediump float;
in vec2 v_texCoord;
layout(location = 0) out vec4 outColor;
uniform sampler2D u_texture;

void main()
{
    outColor = texture(u_texture, v_texCoord);
}

传送带的核心就是图像拷贝操作:

memcpy(m_RenderImage.ppPlane[0], m_SrcImage.ppPlane[0], m_RenderImage.width * m_RenderImage.height * 4 / 2); //左侧预览区域像素拷贝

int bannerHeight = m_RenderImage.height / 2 / m_bannerNum;//一个 banner 的高(小竖条)
int bannerPixelsBufSize = m_RenderImage.width * bannerHeight * 4;//一个 banner 占用的图像内存

uint8 *pBuf = m_RenderImage.ppPlane[0] + m_RenderImage.width * m_RenderImage.height * 4 / 2; //传送带分界线

//从最右侧的竖条图像区域开始拷贝图像
for (int i = m_bannerNum - 1; i >= 1; --i) {
    memcpy(pBuf + i*bannerPixelsBufSize, pBuf + (i - 1)*bannerPixelsBufSize, bannerPixelsBufSize);
}

//将来源区域的像素拷贝到竖条图像区域 0
memcpy(pBuf, pBuf - bannerPixelsBufSize, bannerPixelsBufSize);

渲染操作:

glUseProgram (m_ProgramObj);

glBindVertexArray(m_VaoId);

glUniformMatrix4fv(m_MVPMatLoc, 1, GL_FALSE, &m_MVPMatrix[0][0]);

//图像拷贝,传送带拷贝
memcpy(m_RenderImage.ppPlane[0], m_SrcImage.ppPlane[0], m_RenderImage.width * m_RenderImage.height * 4 / 2);
int bannerHeight = m_RenderImage.height / 2 / m_bannerNum;
int bannerPixelsBufSize = m_RenderImage.width * bannerHeight * 4;

uint8 *pBuf = m_RenderImage.ppPlane[0] + m_RenderImage.width * m_RenderImage.height * 4 / 2; //传送带分界线

for (int i = m_bannerNum - 1; i >= 1; --i) {
    memcpy(pBuf + i*bannerPixelsBufSize, pBuf + (i - 1)*bannerPixelsBufSize, bannerPixelsBufSize);
}
memcpy(pBuf, pBuf - bannerPixelsBufSize, bannerPixelsBufSize);

//更新纹理
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, m_TextureId);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_RenderImage.width, m_RenderImage.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, m_RenderImage.ppPlane[0]);
glBindTexture(GL_TEXTURE_2D, GL_NONE);

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, m_TextureId);
GLUtils::setInt(m_ProgramObj, "u_texture", 0);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, (const void *)0);
glBindVertexArray(GL_NONE);

详细实现代码见项目:github.com/githubhaoha…

到此这篇关于Android OpenGL ES 实现抖音传送带特效的文章就介绍到这了,更多相关Android 抖音传送带特效内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Android自定义HorizontalScrollView实现qq侧滑菜单

    Android自定义HorizontalScrollView实现qq侧滑菜单

    本文主要介绍了android自定义HorizontalScrollView实现qq侧滑菜单的相关知识。具有很好的参考价值。下面跟着小编一起来看下吧
    2017-04-04
  • Android实现地理定位功能

    Android实现地理定位功能

    这篇文章主要为大家详细介绍了Android实现地理定位功能,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-10-10
  • Android自定义格式显示Button的布局思路

    Android自定义格式显示Button的布局思路

    下文的效果都是xml布局文件实现的,一张图片都未曾使用,顺便贴出几个布局文件留个大家参考下,感性的朋友可不要错过了哈
    2013-04-04
  • 详解Android自定义控件属性TypedArray以及attrs

    详解Android自定义控件属性TypedArray以及attrs

    这篇文章主要为大家介绍了android自定义控件属性TypedArray以及attrs,感兴趣的小伙伴们可以参考一下
    2016-01-01
  • Android shape和selector 结合使用实例代码

    Android shape和selector 结合使用实例代码

    本篇文章主要介绍了Android shape和selector 的使用,这里提供了shape 和selector 的详细介绍,并附有代码实例,有兴趣的朋友可以参考下
    2016-07-07
  • 教你制作Android中炫酷的ViewPagerIndicator(不仅仿MIUI)

    教你制作Android中炫酷的ViewPagerIndicator(不仅仿MIUI)

    ViewPagerIndicator作为一款分页指标小部件兼容ViewPager,封装上做得非常不错,目前已为众多知名应用所使用。今天给你大家分享一个炫酷的效果,有需要的可以参考学习。
    2016-08-08
  • Android实现TCP客户端接收数据的方法

    Android实现TCP客户端接收数据的方法

    这篇文章主要介绍了Android实现TCP客户端接收数据的方法,较为详细的分析了Android基于TCP实现客户端接收数据的相关技巧与注意事项,需要的朋友可以参考下
    2016-04-04
  • Android实现自定义飘雪效果

    Android实现自定义飘雪效果

    随着冬季的脚步越来越远,南方的我今年就看了一场雪,下一场雪遥遥无期,那我们来实现一个自定义的 View,它能模拟雪花飘落的景象,所以本文给大家介绍了基于Android实现自定义飘雪效果,感兴趣的朋友可以参考下
    2024-01-01
  • Android界面刷新的方法分享

    Android界面刷新的方法分享

    Android界面刷新的方法分享,需要的朋友可以参考一下
    2013-05-05
  • Android实现拼图游戏

    Android实现拼图游戏

    这篇文章主要为大家详细介绍了Android实现拼图小游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-03-03

最新评论