用 Shader 寫個完美的波浪

前言

皮皮最近接到了一個小需求:

??美術(shù)小姐姐:皮皮皮皮,你能不能做奶茶流济?

??我:锐锣??绳瘟?

??美術(shù)小姐姐:就是那種雕憔,奶茶的輪廓加上動態(tài)水波紋~

??我:嚇?biāo)牢疫€以為讓我做喝的奶茶...

??美術(shù)小姐姐:炒雞多圖片都需要這種效果,用動畫的話工作量太大了糖声!

??我:波浪效果是吧斤彼,小意思,一個月的奶茶就夠了蘸泻,或者掃碼提需求~

??美術(shù)小姐姐:皮琉苇???????

??我:卒~

俗話說:遇事不決,量子力學(xué)寫雖得兒悦施。

根據(jù)我多年喝奶茶的經(jīng)驗并扇,像這種效果用 Shader 做就再簡單不過了,最終的效果如下:

image

趁此機會抡诞,本篇文章就來與小伙伴們分享動態(tài)波浪 Shader 的原理和制作思路吧穷蛹。

要注意的是土陪,這是一篇偏入門的文章,寫得會相對比較詳細(xì)肴熏,盡量讓不懂 Shader 的小白也可以看懂鬼雀,這也是我寫文章的一貫風(fēng)格。

好了蛙吏,話不多說取刃,進入正題~


正文

??整體思路

看到波浪的表現(xiàn)特點我第一時間想到的就是正弦曲線(或者說是正弦波,又讓我想起了示波器)出刷。

??正弦曲線(Sinusoid)

正弦曲線是三角函數(shù)中的一種正弦(Sine)比例的曲線璧疗。正弦曲線表現(xiàn)為一條波浪線,形狀猶如海上完美的波浪馁龟。

標(biāo)準(zhǔn)的正弦函數(shù)公式為:

y = \sin(x)

正弦函數(shù)屬于周期函數(shù)崩侠,其值域為 [-1, 1]

如下圖就是一個純正標(biāo)準(zhǔn)的正弦曲線:

image

而一般我們常用的正弦曲線公式為:

y = A \cdot \sin(\omega x ± \phi) + k

這條公式比標(biāo)準(zhǔn)公式多了幾個常數(shù)坷檩,含義如下:

  • A振幅(Amplitude)却音,曲線最高點與最低點的差值,表現(xiàn)為曲線的整體高度
  • ω角速度(Angular Velocity)矢炼,控制曲線的周期系瓢,表現(xiàn)為曲線的緊密程度
  • φ初相(Initial Phase),即當(dāng) x = 0 時的相位句灌,表現(xiàn)為曲線在坐標(biāo)系上的水平位置
  • k偏距(Offset)夷陋,表現(xiàn)為曲線在坐標(biāo)系上的垂直位置

相位(Phase):上方公式中的 ωx±φ 部分稱為相位,相位發(fā)生在周期性的運動之中胰锌,最直接的理解就是角度骗绕。

?稍加思索

有了公式之后,我們可以嘗試調(diào)整其中的常數(shù)來改變函數(shù)曲線的形態(tài)资昧。

在查看下方的示例時酬土,請嘗試將曲線形態(tài)的變化圖中右上角公式的變化關(guān)聯(lián)起來。

改變曲線的高度

我們可以調(diào)整常數(shù) A(振幅)來改變曲線的值域(值域為 [-A, A]):

image

改變曲線的周期

我們可以調(diào)整常數(shù) ω(角速度)來改變曲線的周期:

image

改變曲線的水平位置

我們可以調(diào)整常數(shù) φ(初相)來改變曲線的水平位置:

image
多說一句

其實對于“曲線的水平位置”這個描述是不太準(zhǔn)確的格带,因為初相實際上改變的是當(dāng) x = 0 時的相位撤缴,也就直接影響函數(shù)曲線在 x = 0 處的位置。

所以說曲線的位置并沒有真正改變叽唱,而只是曲線的形態(tài)發(fā)生了改變屈呕。

但是由于正弦曲線的周期性特點,曲線的這種形態(tài)變化看起來像是曲線進行了位移尔觉。

改變曲線的垂直位置

我們可以調(diào)整常數(shù) k(偏距)來改變曲線的垂直位置:

image

??動手實現(xiàn)

明白了正弦曲線的特性之后凉袱,接下來我們需要做的就是在代碼中運用正弦函數(shù)。

慢著!正弦曲線確實如海上完美的波浪般優(yōu)美专甩,但是正弦曲線是靜態(tài)的钟鸵,我們要的波浪是動態(tài)的啊涤躲!

??如何讓曲線動起來

別慌棺耍!還記得我們可以調(diào)整初相來改變曲線的“水平位置”嗎?

既然如此种樱,我們可以給初相加入時間因素蒙袍,使得 y 值可以隨著時間的增加發(fā)生周期性變化,看起來就像是曲線在進行“水平位移”嫩挤。

就像這樣:

image
得到新的公式

加入時間因素 t 后的曲線公式:

y = A \cdot \sin(\omega x ± \phi t) + k

??On Shadertoy

小貼士:由于 GLSL ES 沒有辦法進行調(diào)試害幅,所以寫 Shader 時可以先在 Shadertoy 中編寫并在線預(yù)覽,顯著提高效率岂昭。

一切盡在注釋中以现,簡單詳細(xì)且直觀。

主函數(shù)代碼如下:

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    // 將像素坐標(biāo)歸一化(區(qū)間 [0.0, 1.0])
    // iResolution 是 Shadertoy 提供的視口分辨率全局變量(類型:vec3)
    vec2 uv = fragCoord / iResolution.xy;
    
    // 振幅(控制波浪頂端和底端的高度)
    float amplitude = 0.05;
    
    // 角速度(控制波浪的周期)
    float angularVelocity = 10.0;
    
    // 頻率(控制波浪移動的速度)
    float frequency = 10.0;
    
    // 偏距(設(shè)為 0.5 使得波浪垂直居中于屏幕)
    float offset = 0.5;
    
    // 初相位(正值表現(xiàn)為向左移動约啊,負(fù)值則表現(xiàn)為向右移動)
    // iTime 是 Shadertoy 提供的運行時間全局變量(類型:float)
    float initialPhase = frequency * iTime;
    
    // 代入正弦曲線公式計算 y 值
    // y = Asin(ωx ± φt) + k
    float y = amplitude * sin((angularVelocity * uv.x) + initialPhase) + offset;
    
    // 區(qū)分 y 值上下部分邑遏,設(shè)置不同顏色
    vec4 color = uv.y > y ? vec4(0.0, 0.0, 0.0, 1.0) : vec4(0.0, 0.7, 0.9, 1.0);
    
    // 輸出到屏幕
    fragColor = color;
}

預(yù)覽效果如下(??是不是有內(nèi)味兒了):

image

在線預(yù)覽:https://www.shadertoy.com/view/ttSfRh

??On Cocos Creator

我們主要關(guān)注片段著色器部分,這里就不展示整個 Effect 文件的代碼了恰矩,直接上傳送門吧记盒。

Effect 文件:https://gitee.com/ifaswind/eazax-ccc/blob/master/resources/effects/eazax-sine-wave.effect

代碼核心其實就是套用了公式,我們代碼注釋一起看吧外傅。

一切盡在注釋中纪吮,簡單詳細(xì)且直觀。

片段著色器代碼如下:

CCProgram fs %{
  precision highp float;

  // 引入 Cocos Creator 內(nèi)置的全部變量
  #include <cc-global>
  
  // 頂點顏色(來自頂點著色器)
  in vec4 v_color;
  // UV 坐標(biāo)(來自頂點著色器)
  in vec2 v_uv0;

  // 紋理
  uniform sampler2D texture;  

  // 自定義屬性
  uniform Properties {
    float amplitude;        // 振幅
    float angularVelocity;  // 角速度
    float frequency;        // 頻率
    float offset;           // 偏距
  };

  void main () {
    // 保存頂點顏色
    vec4 color = v_color;
    
    // 疊加紋理顏色
    color *= texture(texture, v_uv0);
    
    // 直接丟棄原本就透明的像素
    if (color.a == 0.0) discard;
    
    // 初相位(正值表現(xiàn)為向左移動栏豺,負(fù)值則表現(xiàn)為向右移動)
    // cc_time 是 Cocos Creator 提供的運行時間全局變量(類型:vec4)
    float initiaPhase = frequency * cc_time.x;
    
    // 代入正弦曲線公式計算 y 值
    // y = Asin(ωx ± φt) + k
    float y = amplitude * sin(angularVelocity * v_uv0.x + initiaPhase) + offset;
    
    // 丟棄 y 值以上的像素(左上角為原點 [0.0, 0.0])
    if (v_uv0.y < y) discard;
    
    // 輸出顏色
    gl_FragColor = color;
  }
}%

運行效果如下:

image

使用 cc.tween 動態(tài)改變高度(偏距)實現(xiàn)波浪進度條:

cc.tween(this.sineWave)
    .to(3, { height: 1 })
    .to(0.5, { amplitude: 0 })
    .start();
image

在線預(yù)覽:https://ifaswind.gitee.io/eazax-cases?case=sineWave

SineWave 組件:https://gitee.com/ifaswind/eazax-ccc/blob/master/components/effects/SineWave.ts


專題

《一起學(xué) Shader》這個專題斷更了一段時間彬碱,很對不起小伙伴們,是時候續(xù)上了??

《一起學(xué) Shader》

《Shader 入門:GLSL ES(簡介和基本語法)》

《Shader 入門:GLSL ES(數(shù)據(jù)類型)》

《Shader 入門:GLSL ES(運算符和限定符)》


傳送門

微信推文版本

個人博客:菜鳥小棧

開源主頁:陳皮皮

Eazax-CCC 游戲開發(fā)腳手架

Eazax-CCC 示例在線預(yù)覽


更多分享

《為什么選擇使用 TypeScript 奥洼?》

《高斯模糊 Shader》

《一文看懂 YAML》

《Cocos Creator 性能優(yōu)化:DrawCall》

《互聯(lián)網(wǎng)運營術(shù)語掃盲》

《在 Cocos Creator 里畫個炫酷的雷達(dá)圖》


公眾號

??菜鳥小棧

??我是陳皮皮,這是我的個人公眾號晚胡,專注但不僅限于游戲開發(fā)灵奖、前端和后端技術(shù)記錄與分享。

??每一篇原創(chuàng)都非常用心估盘,你的關(guān)注就是我原創(chuàng)的動力瓷患!

Input and output.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市遣妥,隨后出現(xiàn)的幾起案子擅编,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,723評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件爱态,死亡現(xiàn)場離奇詭異谭贪,居然都是意外死亡,警方通過查閱死者的電腦和手機锦担,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評論 2 382
  • 文/潘曉璐 我一進店門俭识,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人洞渔,你說我怎么就攤上這事套媚。” “怎么了磁椒?”我有些...
    開封第一講書人閱讀 152,998評論 0 344
  • 文/不壞的土叔 我叫張陵堤瘤,是天一觀的道長。 經(jīng)常有香客問我浆熔,道長本辐,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,323評論 1 279
  • 正文 為了忘掉前任蘸拔,我火速辦了婚禮师郑,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘调窍。我一直安慰自己宝冕,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,355評論 5 374
  • 文/花漫 我一把揭開白布邓萨。 她就那樣靜靜地躺著地梨,像睡著了一般。 火紅的嫁衣襯著肌膚如雪缔恳。 梳的紋絲不亂的頭發(fā)上宝剖,一...
    開封第一講書人閱讀 49,079評論 1 285
  • 那天,我揣著相機與錄音歉甚,去河邊找鬼万细。 笑死,一個胖子當(dāng)著我的面吹牛纸泄,可吹牛的內(nèi)容都是我干的赖钞。 我是一名探鬼主播,決...
    沈念sama閱讀 38,389評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼聘裁,長吁一口氣:“原來是場噩夢啊……” “哼雪营!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起衡便,我...
    開封第一講書人閱讀 37,019評論 0 259
  • 序言:老撾萬榮一對情侶失蹤献起,失蹤者是張志新(化名)和其女友劉穎洋访,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體谴餐,經(jīng)...
    沈念sama閱讀 43,519評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡姻政,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,971評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了总寒。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片扶歪。...
    茶點故事閱讀 38,100評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖摄闸,靈堂內(nèi)的尸體忽然破棺而出善镰,到底是詐尸還是另有隱情,我是刑警寧澤年枕,帶...
    沈念sama閱讀 33,738評論 4 324
  • 正文 年R本政府宣布炫欺,位于F島的核電站,受9級特大地震影響熏兄,放射性物質(zhì)發(fā)生泄漏品洛。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,293評論 3 307
  • 文/蒙蒙 一摩桶、第九天 我趴在偏房一處隱蔽的房頂上張望桥状。 院中可真熱鬧,春花似錦硝清、人聲如沸辅斟。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽士飒。三九已至,卻和暖如春蔗崎,著一層夾襖步出監(jiān)牢的瞬間酵幕,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評論 1 262
  • 我被黑心中介騙來泰國打工缓苛, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留芳撒,地道東北人。 一個月前我還...
    沈念sama閱讀 45,547評論 2 354
  • 正文 我出身青樓未桥,卻偏偏與公主長得像番官,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子钢属,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,834評論 2 345