欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android OpenGL ES 實現(xiàn)抖音傳送帶特效(原理解析)

 更新時間:2022年07月21日 09:48:11   作者:字節(jié)流動  
這篇文章主要介紹了Android OpenGL ES 實現(xiàn)抖音傳送帶特效,抖音傳送帶特效推出已經(jīng)很長一段時間了,前面也實現(xiàn)了下,最近把它整理出來了,如果你有仔細觀測傳送帶特效,就會發(fā)現(xiàn)它的實現(xiàn)原理其實很簡單,需要的朋友可以參考下

抖音 APP 真是個好東西,不過也容易上癮,老實說你的抖音是不是反復卸載又反復安裝了,后來我也發(fā)現(xiàn)我的幾個 leader 都不刷抖音,這令我挺吃驚的。

我刷抖音主要是為了看新聞,聽一些大 V 講歷史,研究抖音的一些算法特效,最重要的是抖音提供了一個年輕人的視角去觀察世界。另外,自己感興趣的內容看多了,反而訓練抖音推送更多類似的優(yōu)質內容,大家可以反向利用抖音的這一特點。

至于我的 leader 老是強調刷抖音不好,對此我并不完全認同。

抖音傳送帶特效原理

抖音傳送帶特效推出已經(jīng)很長一段時間了,前面也實現(xiàn)了下,最近把它整理出來了,如果你有仔細觀測傳送帶特效,就會發(fā)現(xiàn)它的實現(xiàn)原理其實很簡單。

通過仔細觀察抖音的傳送帶特效,你可以發(fā)現(xiàn)左側是不停地更新預覽畫面,右側看起來就是一小格一小格的豎條狀圖像區(qū)域不斷地向右移動,一直移動到右側邊界位置。

預覽的時候每次拷貝一小塊預覽區(qū)域的圖像送到傳送帶,這就形成了源源不斷地向右傳送的效果。

原理圖進行了簡化處理, 實際上右側的豎條圖像更多,效果會更流暢,每來一幀預覽圖像,首先拷貝更新左側預覽畫面,然后從最右側的豎條圖像區(qū)域開始拷貝圖像(想一想為什么?)。

例如將區(qū)域 2 的像素拷貝到區(qū)域 3 ,然后將區(qū)域 1 的像素拷貝到區(qū)域 2,以此類推,最后將來源區(qū)域的像素拷貝到區(qū)域 0 。

這樣就形成了不斷傳送的效果,最后將拷貝好的圖像更新到紋理,利用 OpenGL 渲染到屏幕上。

抖音傳送帶特效實現(xiàn)

上節(jié)原理分析時,將圖像區(qū)域從左側到右側拷貝并不高效,可能會導致一些性能問題,好在 Android 相機出圖都是橫向的(旋轉了 90 或 270 度),這樣圖像區(qū)域上下拷貝效率高了很多,最后渲染的時候再將圖像旋轉回來。

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); //左側預覽區(qū)域像素拷貝

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; //傳送帶分界線

//從最右側的豎條圖像區(qū)域開始拷貝圖像
for (int i = m_bannerNum - 1; i >= 1; --i) {
    memcpy(pBuf + i*bannerPixelsBufSize, pBuf + (i - 1)*bannerPixelsBufSize, bannerPixelsBufSize);
}

//將來源區(qū)域的像素拷貝到豎條圖像區(qū)域 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);

詳細實現(xiàn)代碼見項目:github.com/githubhaoha…

到此這篇關于Android OpenGL ES 實現(xiàn)抖音傳送帶特效的文章就介紹到這了,更多相關Android 抖音傳送帶特效內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

最新評論