在 iOS 中使用 GLSL 實現抖音特效

2021-02-20 知識小集

本文通過模仿抖音中幾種特效的實現,來講解 GLSL 的實際應用。

前言

本文的靈感來自於 《當一個 Android 開發玩抖音玩瘋了之後(二)》[1]這篇文章。

這位博主在 Android 平臺上,通過自己的分析,嘗試還原了抖音上的幾種視頻特效。他是通過「部分 GLSL 代碼 + 部分 Java 代碼」的方式來實現的。

讀完之後,在膜拜之餘,我產生了一個大膽的想法:我可不可以在 iOS 上,只通過純 GLSL 的編寫,來實現類似的效果呢?

很好的想法,不過,由於抖音的特效是基於視頻的濾鏡,我們在這之前只講到了關於圖片的渲染,如果馬上跳躍到視頻的部分,好像有點超綱了。

於是,我又有了一個更大膽的想法:我可不可以在 iOS 上,只通過純 GLSL 的編寫,在靜態的圖片上,實現類似的效果呢?

這樣的話,我們就可以把更多的注意力放在 GLSL 本身,而不是視頻的採集和輸出上面。

於是,就有了這篇文章。為了無縫地過渡,我會沿用之前 GLSL 渲染的例子[2],只改變 Shader 部分的代碼,來嘗試還原那篇文章中實現的六種特效。

〇、動畫

你可能會問:抖音上的特效都是動態的,要怎麼把動態的效果,加到一個靜態的圖片上呢?

問的好,所以第一步,我們就要讓靜態的圖片動起來。

回想一下,我們在 UIKit 中實現的動畫,無非就是把指令發送給 CoreAnimation,然後在屏幕刷新的時候,CoreAnimation 會去逐幀計算當前應該顯示的圖像。

這裡的重點是「逐幀計算」。在 OpenGL ES 中也是類似,我們實現動畫的方式,就是自己去計算每一幀應該顯示的圖像,然後在屏幕刷新的時候,重新渲染。

這個「逐幀計算」的過程,我們是放到 Shader 中進行的。然後我們可以通過一個表示時間的參數,在重新渲染的時候,傳入當前的時間,讓 Shader 計算出當前動畫的進度。至於重新渲染的時機,則是依靠 CADisplayLink 來實現的。

具體代碼大概像這樣:

self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(timeAction)];
[self.displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];


- (void)timeAction {
    glUseProgram(self.program);
    glBindBuffer(GL_ARRAY_BUFFER, self.vertexBuffer);

    
    CGFloat currentTime = self.displayLink.timestamp - self.startTimeInterval;
    GLuint time = glGetUniformLocation(self.program, "Time");
    glUniform1f(time, currentTime);

    
    glClear(GL_COLOR_BUFFER_BIT);
    glClearColor(1, 1, 1, 1);

    
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
    [self.context presentRenderbuffer:GL_RENDERBUFFER];
}

相應地,在 Shader 中有一個 uniform 修飾的 Time 參數:

uniform float Time;

這樣 Shader 就可以通過 Time 來計算出當前應該顯示的圖像了。

一、縮放1、最終效果

我們要實現的第一種效果是「縮放」,看起來很簡單,可以通過修改頂點坐標和紋理坐標的對應關係來實現。

這是一個很基礎的效果,在下面的其它特效中還會用到。修改坐標的對應關係可以通過修改頂點著色器,或者修改片段著色器來實現。 這裡先講修改頂點著色器的方式,在後面的特效中會再提一下修改片段著色器的方式。

2、代碼實現

頂點著色器代碼:

attribute vec4 Position;
attribute vec2 TextureCoords;
varying vec2 TextureCoordsVarying;

uniform float Time;

const float PI = 3.1415926;

void main (void) {
    float duration = 0.6;
    float maxAmplitude = 0.3;

    float time = mod(Time, duration);
    float amplitude = 1.0 + maxAmplitude * abs(sin(time * (PI / duration)));

    gl_Position = vec4(Position.x * amplitude, Position.y * amplitude, Position.zw);
    TextureCoordsVarying = TextureCoords;
}

這裡的 duration 表示一次縮放周期的時長,mod(Time, duration)表示將傳入的時間轉換到一個周期內,即 time 的範圍是 0 ~ 0.6,amplitude 表示振幅,引入 PI 的目的是為了使用 sin 函數,將 amplitude 的範圍控制在 1.0 ~ 1.3 之間,並隨著時間變化。

這裡放大的關鍵在於 vec4(Position.x * amplitude, Position.y * amplitude, Position.zw),我們將頂點坐標的 x 和 y 分別乘上一個放大係數,在紋理坐標不變的情況下,就達到了拉伸的效果。

二、靈魂出竅1、最終效果

「靈魂出竅」看上去是兩個層的疊加,並且上面的那層隨著時間的推移,會逐漸放大且不透明度逐漸降低。這裡也用到了放大的效果,我們這次用片段著色器來實現。

2、代碼實現

片段著色器代碼:

precision highp float;

uniform sampler2D Texture;
varying vec2 TextureCoordsVarying;

uniform float Time;

void main (void) {
    float duration = 0.7;
    float maxAlpha = 0.4;
    float maxScale = 1.8;

    float progress = mod(Time, duration) / duration; 
    float alpha = maxAlpha * (1.0 - progress);
    float scale = 1.0 + (maxScale - 1.0) * progress;

    float weakX = 0.5 + (TextureCoordsVarying.x - 0.5) / scale;
    float weakY = 0.5 + (TextureCoordsVarying.y - 0.5) / scale;
    vec2 weakTextureCoords = vec2(weakX, weakY);

    vec4 weakMask = texture2D(Texture, weakTextureCoords);

    vec4 mask = texture2D(Texture, TextureCoordsVarying);

    gl_FragColor = mask * (1.0 - alpha) + weakMask * alpha;
}

首先是放大的效果。關鍵點在於 weakX 和 weakY 的計算,比如 0.5 + (TextureCoordsVarying.x - 0.5) / scale這一句的意思是,將頂點坐標對應的紋理坐標的 x 值到紋理中點的距離,縮小一定的比例。這次我們是改變了紋理坐標,而保持頂點坐標不變,同樣達到了拉伸的效果。

然後是兩層疊加的效果。通過上面的計算,我們得到了兩個紋理顏色值 weakMask 和 mask, weakMask 是在 mask 的基礎上做了放大處理。

我們將兩個顏色值進行疊加需要用到一個公式:最終色 = 基色 * a% + 混合色 * (1 - a%),這個公式來自 混合模式中的正常模式[3]。

這個公式表明了一個不透明的層和一個半透明的層進行疊加,重疊部分的最終顏色值。因此,上面疊加的最終結果是 mask * (1.0 - alpha) + weakMask * alpha

三、抖動1、最終效果

「抖動」是很經典的抖音的顏色偏移效果,其實這個效果實現起來還挺簡單的。另外,除了顏色偏移,可以看到還有微弱的放大效果。

2、代碼實現

片段著色器代碼:

precision highp float;

uniform sampler2D Texture;
varying vec2 TextureCoordsVarying;

uniform float Time;

void main (void) {
    float duration = 0.7;
    float maxScale = 1.1;
    float offset = 0.02;

    float progress = mod(Time, duration) / duration; 
    vec2 offsetCoords = vec2(offset, offset) * progress;
    float scale = 1.0 + (maxScale - 1.0) * progress;

    vec2 ScaleTextureCoords = vec2(0.5, 0.5) + (TextureCoordsVarying - vec2(0.5, 0.5)) / scale;

    vec4 maskR = texture2D(Texture, ScaleTextureCoords + offsetCoords);
    vec4 maskB = texture2D(Texture, ScaleTextureCoords - offsetCoords);
    vec4 mask = texture2D(Texture, ScaleTextureCoords);

    gl_FragColor = vec4(maskR.r, mask.g, maskB.b, mask.a);
}

這裡的放大和上面類似,我們主要看一下顏色偏移。顏色偏移是對三個顏色通道進行分離,並且給紅色通道和藍色通道添加了不同的位置偏移,代碼很容易看懂。

四、閃白1、最終效果

「閃白」其實看起來一點兒也不酷炫,而且看久了還容易被閃瞎。這個效果實現起來也十分簡單,無非就是疊加一個白色層,然後白色層的透明度隨著時間不斷地變化。

2、代碼實現

片段著色器代碼:

precision highp float;

uniform sampler2D Texture;
varying vec2 TextureCoordsVarying;

uniform float Time;

const float PI = 3.1415926;

void main (void) {
    float duration = 0.6;

    float time = mod(Time, duration);

    vec4 whiteMask = vec4(1.0, 1.0, 1.0, 1.0);
    float amplitude = abs(sin(time * (PI / duration)));

    vec4 mask = texture2D(Texture, TextureCoordsVarying);

    gl_FragColor = mask * (1.0 - amplitude) + whiteMask * amplitude;
}

在上面「靈魂出竅」的例子中,我們已經知道了如何實現兩個層的疊加。這裡我們只需要創建一個白色的層 whiteMask,然後根據當前的透明度來計算最終的顏色值即可。

五、毛刺1、最終效果

終於有了一個稍微複雜一點的效果,「毛刺」看上去是「撕裂 + 微弱的顏色偏移」。顏色偏移我們在上面已經實現,這裡主要是講解撕裂的效果。

具體的思路是,我們讓每一行像素隨機偏移 -1 ~ 1 的距離(這裡的 -1 ~ 1 是對於紋理坐標來說的),但是如果整個畫面都偏移比較大的值,那我們可能都看不出原來圖像的樣子。所以我們的邏輯是,設定一個閾值,小於這個閾值才進行偏移,超過這個閾值則乘上一個縮小係數

則最終呈現的效果是:絕大部分的行都會進行微小的偏移,只有少量的行會進行較大偏移

2、代碼實現

片段著色器代碼:

precision highp float;

uniform sampler2D Texture;
varying vec2 TextureCoordsVarying;

uniform float Time;

const float PI = 3.1415926;

float rand(float n) {
    return fract(sin(n) * 43758.5453123);
}

void main (void) {
    float maxJitter = 0.06;
    float duration = 0.3;
    float colorROffset = 0.01;
    float colorBOffset = -0.025;

    float time = mod(Time, duration * 2.0);
    float amplitude = max(sin(time * (PI / duration)), 0.0);

    float jitter = rand(TextureCoordsVarying.y) * 2.0 - 1.0; 
    bool needOffset = abs(jitter) < maxJitter * amplitude;

    float textureX = TextureCoordsVarying.x + (needOffset ? jitter : (jitter * amplitude * 0.006));
    vec2 textureCoords = vec2(textureX, TextureCoordsVarying.y);

    vec4 mask = texture2D(Texture, textureCoords);
    vec4 maskR = texture2D(Texture, textureCoords + vec2(colorROffset * amplitude, 0.0));
    vec4 maskB = texture2D(Texture, textureCoords + vec2(colorBOffset * amplitude, 0.0));

    gl_FragColor = vec4(maskR.r, mask.g, maskB.b, mask.a);
}

上面提到的像素隨機偏移需要用到隨機數,可惜 GLSL 裡並沒有內置的隨機函數,所以我們需要自己實現一個。

這個 float rand(float n)的實現看上去很神奇,它其實是來自 這裡[4],江湖人稱「噪聲函數」。

它其實是一個偽隨機函數,本質上是一個 Hash 函數。但在這裡我們可以把它當成隨機函數來使用,它的返回值範圍是 0 ~ 1。如果你對這個函數想了解更多的話可以看 這裡[5]。

六、幻覺1、最終效果

「幻覺」這個效果有點一言難盡,因為其實看上去並不是很像。原來的效果是基於視頻上一幀的結果去合成,靜態的圖片很難模擬出這種情況。不管怎麼說,既然已經盡力,不像就不像吧,下面講一下我的實現思路。

可以看出這個效果是殘影和顏色偏移的疊加。

殘影的效果還好,在移動的過程中,每經過一段時間間隔,根據當前的位置去創建一個新層,並且新層的不透明度隨著時間逐漸減弱。於是在一個移動周期內,可以看到很多透明度不同的層疊加在一起,從而形成殘影的效果。

然後是這個顏色偏移。我們可以看到,物體移動的過程是藍色在前面,紅色在後面。所以整個過程可以理解成:在移動的過程中,每間隔一段時間,遺失了一部分紅色通道的值在原來的位置,並且這部分紅色通道的值,隨著時間偏移,會逐漸恢復。

2、代碼實現

片段著色器代碼:

precision highp float;

uniform sampler2D Texture;
varying vec2 TextureCoordsVarying;

uniform float Time;

const float PI = 3.1415926;
const float duration = 2.0;

vec4 getMask(float time, vec2 textureCoords, float padding) {
    vec2 translation = vec2(sin(time * (PI * 2.0 / duration)),
                            cos(time * (PI * 2.0 / duration)));
    vec2 translationTextureCoords = textureCoords + padding * translation;
    vec4 mask = texture2D(Texture, translationTextureCoords);

    return mask;
}

float maskAlphaProgress(float currentTime, float hideTime, float startTime) {
    float time = mod(duration + currentTime - startTime, duration);
    return min(time, hideTime);
}

void main (void) {
    float time = mod(Time, duration);

    float scale = 1.2;
    float padding = 0.5 * (1.0 - 1.0 / scale);
    vec2 textureCoords = vec2(0.5, 0.5) + (TextureCoordsVarying - vec2(0.5, 0.5)) / scale;

    float hideTime = 0.9;
    float timeGap = 0.2;

    float maxAlphaR = 0.5; 
    float maxAlphaG = 0.05; 
    float maxAlphaB = 0.05; 

    vec4 mask = getMask(time, textureCoords, padding);
    float alphaR = 1.0; 
    float alphaG = 1.0; 
    float alphaB = 1.0; 

    vec4 resultMask;

    for (float f = 0.0; f < duration; f += timeGap) {
        float tmpTime = f;
        vec4 tmpMask = getMask(tmpTime, textureCoords, padding);
        float tmpAlphaR = maxAlphaR - maxAlphaR * maskAlphaProgress(time, hideTime, tmpTime) / hideTime;
        float tmpAlphaG = maxAlphaG - maxAlphaG * maskAlphaProgress(time, hideTime, tmpTime) / hideTime;
        float tmpAlphaB = maxAlphaB - maxAlphaB * maskAlphaProgress(time, hideTime, tmpTime) / hideTime;

        resultMask += vec4(tmpMask.r * tmpAlphaR,
                           tmpMask.g * tmpAlphaG,
                           tmpMask.b * tmpAlphaB,
                           1.0);
        alphaR -= tmpAlphaR;
        alphaG -= tmpAlphaG;
        alphaB -= tmpAlphaB;
    }
    resultMask += vec4(mask.r * alphaR, mask.g * alphaG, mask.b * alphaB, 1.0);

    gl_FragColor = resultMask;
}

從代碼的行數可以看出,這個效果應該是裡面最複雜的。為了實現殘影,我們先讓圖片隨時間做圓周運動。

vec4 getMask(float time, vec2 textureCoords, float padding)這個函數可以計算出,在某個時刻圖片的具體位置。通過它我們可以每經過一段時間,去生成一個新的層。

float maskAlphaProgress(float currentTime, float hideTime, float startTime)這個函數可以計算出,某個時刻創建的層,在當前時刻的透明度。

maxAlphaR 、 maxAlphaG 、 maxAlphaB 分別指定了新層初始的三個顏色通道的透明度。因為最終的效果是殘留紅色,所以主要保留了紅色通道的值。

然後是疊加,和兩層疊加的情況類似,這裡通過 for 循環來累加每一層的每個通道乘上自身的透明度的值,算出最終的顏色值 resultMask 。

註: 在 iOS 的模擬器上,只能用 CPU 來模擬 GPU 的功能。所以在模擬器上運行上面的代碼時,可能會十分卡頓。尤其是最後這個效果,由於計算量太大,親測模擬器顯示不出來。因此如果要跑代碼,最好使用真機運行。

源碼

請到 GitHub (https://github.com/lmf12/blog-demo/tree/master/testOpenGLESFilter) 上查看完整代碼。

參考

https://www.jianshu.com/p/5bb7f2a0da90

http://www.lymanli.com/2019/02/17/ios-opengles-render-texture/

https://baike.baidu.com/item/%E6%B7%B7%E5%90%88%E6%A8%A1%E5%BC%8F/6700481

https://gist.github.com/patriciogonzalezvivo/670c22f3966e662d2f83

https://xiaoiver.github.io/coding/2018/08/01/%E5%99%AA%E5%A3%B0%E7%9A%84%E8%89%BA%E6%9C%AF.html

相關焦點

  • 抖音大頭漫畫特效在哪?怎麼錄製?ios漫畫製作
    抖音大頭漫畫特效在哪?怎麼錄製?ios漫畫製作;老郭發現最近這段時間抖粉們在刷抖音的時候,總是能刷到一些卡通動漫大頭特效的視頻,這個新花樣玩法很炫酷,現在已然成了抖音熱門,因此受到抖粉們的熱捧,抖粉們發現在抖音系統自帶的特效裡並沒有這個,那麼,這個卡通動漫大頭特效是怎麼錄製的呢?
  • 抖音裡面的變身特效怎麼玩 抖音變身特效可以實現嗎
    18183首頁 抖音裡面的變身特效怎麼玩 抖音變身特效可以實現嗎 抖音裡面的變身特效怎麼玩 抖音變身特效可以實現嗎
  • 抖音證件照特效是哪一個在哪用 抖音怎麼證件照P圖教程
    抖音證件照特效是哪一個?想要P出好看的證件照用抖音怎麼進行操作呢?相信有不少朋友都被很喜歡火爆的證件照特效,自己也想要嘗試但是不知道在哪裡使用,下面小編就為大家帶來詳細的介紹,幫助大家順利的完成體驗。
  • 抖音長腿特效功能在哪裡怎麼用 拍出長腿特效使用方法
    這兩天抖音又更新了,這次主要是更新了一個長腿特效,可以讓你秒變大長腿的特效喲,那麼這個特效要怎麼用呢,一起來看看吧~  抖音長腿特效怎麼用 使用方法詳細介紹  首先,打開抖音APP,點擊「+」,選擇你想拍攝的歌曲進行拍攝;
  • 抖音Effect Creator軟體怎麼用教程 特效道具使用方法介紹
    抖音道具Effect Creator是一個製作特效的軟體,各種各樣的特效一鍵製成,非常實用的哦!但是具體的怎麼用還是不了解,所以小編為大家介紹一下!  抖音道具Effect Creator怎麼用  Effect Creator可以為抖音短視頻App相機生產特效,它支持合成特效並實時預覽。
  • 抖音鬍子特效在哪裡 怎麼加絡腮鬍子特效方法
    抖音鬍子特效在哪裡?鬍子特效視頻怎麼拍?還不知道方法的玩家們,下面小編就為大家帶來具體介紹,接著往下看吧。  抖音鬍子特效在哪裡?怎麼加絡腮鬍子特效方法  一、鬍子特效  在快手APP中大家可以找到這樣的一款特效,並非是抖音本地的特效,相信抖音會在未來不久推出類似的特效,不過在此之前大家可以使用快手拍攝;  二、特效位置  打開抖音之後,可以點擊右上角的【視頻】按鈕進入拍攝界面,在拍攝左下角的【魔法】中可以找到這個特效哦
  • GL Shader Language(GLSL)詳解-基礎語法
    在可編程管線中,必須使用GLSL編寫頂點著色器和片源著色器,而GLSL的語法和C語言有很多相似之處,本篇文章將介紹GLSL的基礎語法。更多細節和說明可以查閱官方文檔 http://www.opengl.org/sdk/docs/man/ http://www.opengl.org/registry/doc/GLSLangSpec.Full.1.20.8.pdf 在VSCode中可以創建後綴為.glsl的文件編寫著色器代碼,並通過ShaderToy進行實時預覽調試。
  • 抖音超大頭特效是哪個在哪 大頭特效怎麼用拍攝教程
    抖音超大頭特效現在很好,我們看到很多人都在發同款特效。其實這款特效就是抖音自帶的道具,可以把我們的頭部進行放大,看清我們的細微表情。而抖音超大頭特效怎麼拍呢?一起來看看吧。  抖音超大頭特效怎麼拍  這個視頻的特效道具就是抖音裡的,在抖音裡點擊底部的【+】選項。
  • 抖音上煙霧特效怎麼拍攝 抖音上煙霧特效製作方法分享
    抖音上煙霧特效怎麼拍攝? 煙霧特效BGM是什麼?大家需要下載抖音推的第二軟體趣推或者pixaloopAPP來完成。  抖音上好玩的特效非常多,各個都被網友們玩出了新花樣,最近最火的特效就是泫雅仿妝,然後阿麗塔。不過最近新進了一個煙霧的特效,配上震撼的音樂也是酷到不行,下面我們就一起來看看吧!
  • 抖音裡很火的小仙女特效怎麼拍 抖音魔法棒特效在哪裡
    18183首頁 抖音裡很火的小仙女特效怎麼拍 抖音魔法棒特效在哪裡 抖音裡很火的小仙女特效怎麼拍 抖音魔法棒特效在哪裡
  • 抖音上超火的酷炫特效,用這個殘影分身效果就能實現!
    抖音上很火的酷炫特效,殘影分身效果:添加一個愛剪輯的轉場特效,就能快速Get!這種有顏色變化的殘影分身效果,用在動感視頻中,視覺效果會更棒。比如運動、舞蹈、旅拍視頻等。製作攻略:1.一鍵添加「奇幻顏色特效」轉場特效,實現酷炫殘影分身效果點擊主界面頂部的「轉場特效」功能,在「已添加片段」處選中目標視頻片段,一鍵添加「奇幻顏色特效」轉場特效。
  • iOS新浪新聞首頁卡片滾動特效實現淺談
    Leader非常中意這個特效,說要在我們app中使用,問我能不能實現,我一看這個特效,臥槽,涉及了好多技術點,實現難度很大。如果是自己做特效還好,模仿別人的特效,做的粗糙沒什麼,如果要做得好,就得考慮好多細節。身為男人,怎麼能說不行,自己給自己安排時間,偷偷搞,經過了幾天的艱苦奮戰,終於完成了,開森。
  • 抖音白菜葉敷面膜特效在哪怎麼拍 抖音近日最火特效盤點
    如今在抖音中出現了很多特效視頻,特別是那些美妝的特效,讓原本就很漂亮的小姐姐因為特效的加入,而變得更加迷人了。要說最火的特效,那一定是阿麗塔和被玩壞的草莓了。這兩個特效會讓拍抖音的人一下子愛上鏡頭的自己,也會讓刷抖音的人為你瘋狂點讚。
  • 抖音卡通動漫大頭果特效在哪 蘋果手機怎麼沒這個功能原因
    抖音卡通動漫大頭特效在哪?在抖音裡面很多小夥伴發現裡面有一些有趣的東西,比如大頭特效是在抖音哪個功能裡有,一起來看看吧!  手機怎麼沒有卡通動漫大頭特效原因  1、這個功能目前只有蘋果系統能用;  2、蘋果手機需要把系統升級到ios12
  • 抖音油畫特效怎麼用在哪裡 油畫特效是用什麼軟體弄的介紹
    在看到抖音的小夥伴老是看到一些獨特的油畫效果,那麼你知道怎麼製作而成的嗎?為此我們給你帶來簡單輕鬆的使用操作技巧,讓你可以輕鬆的打造出自己想要的油畫特效豔樣式。  抖音油畫特效使用操作方法  1、首先可以對於最新版的剪映進行下載,在軟體中有非常好用的模板等你來使用。
  • 抖音甩頭髮慢動作視頻怎麼拍 此特效APP使用推薦
    最近我們常常可以在抖音上看到放慢了的甩頭發動作,那麼這樣的視頻該怎麼拍呢?本文小編會為大家詳細介紹,感興趣的小夥伴們,一起來看看抖音甩頭髮慢動作是用了什麼特效吧!  抖音甩頭髮慢動作是用了什麼特效  其實是用了一款名為【video star】的APP。
  • 短視頻APP快手上線「變小孩」特效 iOS及安卓均支持
    8月14日,短視頻平臺快手上線名為「變小孩」的魔法表情,在拍攝界面選擇該款魔法表情,就能體驗變童顏的特效。依賴生成技術的這款魔錶真實感極強。據了解,該魔錶適配任意ios及Android設備,支持絕大部分機型手機端上實時生成視頻版娃娃臉。
  • 抖音我是一顆跳跳糖大頭特效是怎麼拍的 大頭特效背景音樂是什麼
    [海峽網]現如今在抖音上最火的就是各種特效視頻了,不管是增加顏值的阿麗塔妝容,還是可愛的草莓妝,都讓很多人愛上這些特效了。最近有一個大頭特效再一次流行起來了,很多人好奇這個特效在哪裡可以拍攝?要用什麼軟體拍?
  • 字體超市客戶端新版本更新,抖音特效字體來了!
    上個月,字體超市上線了良心字體工具字體超市管家,只要在電腦上安裝這一工具之後,大家無需下載字體,就能實現一鍵換字了,而且是海量字體隨便換。應廣大用戶的需求,字體超市管家近日再度更新,新增了兩大超香的功能,包括:特效字體,以及新支持AI、AE、ID軟體一鍵換字功能,滿足用戶的多場景和多端使用需求。
  • 抖音寶寶特效在哪怎麼弄 吳亦凡同款寶寶特效
    今天吳亦凡寶寶特效上了微博熱搜,很多人好奇這個寶寶特效是在哪裡弄的?其實這是抖音上面的一個特效,下面小編就給大家介紹下抖音寶寶特效在哪怎麼弄方法。  抖音寶寶特效在哪怎麼弄  1.首先,打開手機上的抖音短視頻,一定要是最新版本的。