Android 音影片開發【特效篇】【一】抖音傳送帶特效 | 8月更文挑戰
這是我參與8月更文挑戰的第6天,活動詳情檢視:8月更文挑戰
本章將介紹如何實現抖音
傳送帶
特效
一、實現效果
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 著色器
頂點著色器
c++
attribute vec4 aPos;
attribute vec2 aCoordinate;
varying vec2 vCoordinate;
void main(){
vCoordinate = aCoordinate;
gl_Position = aPos;
}
關於頂點著色器,並沒有做任何特殊處理
片元著色器
c++
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
即可
c++
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
保留上一幀資料,從而在下一次渲染時能夠使用
java
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
- 使用 GO-CQHttp或mirai框架 搭建QQ的機器人
- Android 音影片開發【影片篇】【二】影片採集 | 8月更文挑戰
- Android 音影片開發【特效篇】【一】抖音傳送帶特效 | 8月更文挑戰
- 如何打造一款高效能的全屏紅包雨
- 新型Enemybot DDoS殭屍網路借用Mirai和Gafgyt攻擊程式碼
- 惡意軟體Mirai正積極利用Spring4Shell漏洞
- Linux 惡意軟體呈上升趨勢:XorDDoS、Mirai 和 Mozi 最流行
- Python mirai開發QQ機器人起步教程(2021.9.9測試有效)
- 通報:Confluence遠端程式碼執行漏洞(CVE-2021-26084)被黑產大規模利用
- 通報:騰訊主機安全捕獲YAPI遠端程式碼執行0day漏洞在野利用,該攻擊正在擴散,可使用防火牆阻截
- Mirai_ptea Botnet利用KGUARD DVR未公開漏洞報告
- 研究人員通過 Mirai 惡意軟體有效載荷確定了兩個新的物聯網漏洞
- 研究人員發現了另一種針對新物聯網漏洞的Mirai變種
- 防禦DDoS措施種類太多選暈了頭?學會利用他人經驗做對的選擇
- 豐田將成汽車界諾基亞?
- HTB-靶機-Mirai
- 黑客通常可以分為以下8種類型
- 研究人員通過 Mirai 惡意軟體 payload 確定了兩個新的 IoT 漏洞
- 開源一款QQ機器人