前言

玩過ShaderToy的肯定都聽過iq這個名字,即使沒有聽過,肯定也見過他的一些作品,比如隨便截兩張:

千萬不要誤會,上圖中的兩個效果可不是建模渲染的展示,而是由純GLSL語言(說純語言不準確,倒是有用到一些紋理圖片)生成的哦,並且還是有動態效果的?

有興趣的可以去他的個人主頁觀摩下,同時iq也是ShaderToy的創始人,在上面創作了非常多很有啟發性的作品。

好了,讓我們回到殘酷的現實中來,先從iq大神的一篇極其簡單的作品開始吧?


核心實現

為了更準確的表達此篇文章的內容,我會在移植到Unity中時做一些刪減與修改,以便讀者能更好的理解與閱讀。

先放上這次的最終效果視頻:

視頻封面

00:08

UV準備

頂點著色器中與以往一樣,沒有什麼特別的,我們重點來關注下片斷著色器。

i.uv 表示的是屏幕上0-1的歸一化坐標, ScreenParams.xy表示的是屏幕的長寬像素,而i.uv*ScreenParams即可得到屏幕上的每個像素點的坐標。

所得到值再除以_ScreenParams.x(注意這裡是.x),即可得到以寬像素為基準的UV分佈圖。

細觀察得到的uv值,在x的值上是0-1的值,但是在y值上卻不是0-1,看效果感覺是0-0.45左右,這就是我們除以_ScreenParams.x得到的效果,目的是為了使畫面不變形,以實際像素為基準進行表現。

二維向量p根據式子拆分後可以看出是為了得到以下的動畫效果:

其中p.x在後面會表示為方格到細胞格的變化因子。

p.y則表示為清晰到模糊的變化因子。

return iqnoise(24*uv,p.x,p.y);

是我們最終返回的顏色值,其中iqnosie是我們自定義的一個函數,是整個效果的核心實現,代碼如下:

按照執行順序,我們先來分析下iqnoise函數。

float2 p = floor(x);

floor(x),表示對x值進行向下取整。

這裡的x值就是frag中調用iqnoise方法的第一個參數24*uv,也就是將現有的uv乘以24倍。

float2 f = frac(x);

frac(x),返回x值的小數部分

對24倍的uv取小數,其實就是將其分成24份獨立的小uv塊,y軸上由於上面做了特珠處理,所以不會是24份,同時保證了不變形。

方格的清晰與模糊

由於p.x與p.y分別表示了格子的變化與模糊的變化,那麼為了更直觀的分析,讓我們假設p.x=0時,p.y保持原有不變,則效果如下:

視頻封面

00:04

iqnoise同步修改後部分代碼如下:

k值先忽略,繼續往下看。。。

分別定義了va與wt,都初始化為0

然後開始一個5x5的嵌套循環,值域如下:

定義二維向量g,值為當前循環中的坐標點

定義三維向量o,默認初始化為0

o的z值等於hash3(p2+g).z;

也就是當計算當前像素點時,會對此像素周圍的24個像素點分別取值採樣(是不是感覺和我們平時做圖像模糊時用到的卷積演算法很相似?)。

好,現在讓我們來看下自定義的隨機函數hash3.

隨機馬賽克

hash3函數,它的主要作用是為了返回隨機的方格效果

在之前的案例中我們有知道隨機噪波的常用公式為:

frac(sin(dot(i.uv.xy, float2(12.9898, 78.233))) * 43758.5453);

其中dot()的部分在我們這裡是用q來表示,q是三維向量,分別代表著RGB三個不同的通道。

以R通道為例,可拆解為:

q.r=frac(sin(dot(p2,float2(127.1,311.7)))*43758.5453);

效果如下:

可以看出其實就是通過p2的UV特性來實現類似這種馬賽克的隨機效果。

通過在dot中指定不同的數值來實現三個通道不一致的效果,最終RGB效果如下:

不錯的彩色馬賽克效果,是不是又學會了一招~

不得不說,ShaderToy中處處有驚喜!

其實hash3中我們也可以改成這樣的簡化版:

不過只有單通道,並且顏色在取值上會偏暗些。

繼續循環

好了,hash3瞭解完畢後,讓我們繼續回到上面的循環嵌套中

定義二維向量r,值為g-f,並對其做點積運算,同時利用smoothstep來實現柔和過渡效果,而這裡的k就是決定模糊程度的。

float k = 1.0+63.0*pow(1.0-p.y,4.0);

可以得出k的取值在(1-64)之間,隨著時間變化的曲線如下圖所示:

峯谷時為模糊效果,持續一段模糊後會向清晰過渡,然後再過渡回來,就是這樣的一個循環。

va與wt就是利用卷積演算法來實現加權平均後最終的像素結果。

由此可見iqnoise中的u與v值不同時可以得到不同的效果,有以下幾種情況:

return iqnoise( 24.0*uv, 0,0 );

return iqnoise( 24.0*uv, 0,1 );

return iqnoise( 24.0*uv, 1,0 );

return iqnoise( 24.0*uv, 1,1 );

也就是說如果u值是0-1間的動畫,那麼結果就是方格到細胞格的過渡動畫效果

同樣v值如果是0-1的動畫,那麼結果就是清晰到模糊的過渡動畫

同時兩者也可以互相組合,最終得出本例的最終效果。


最後

歡迎大家關注更多乾貨的公眾號:Unity技術美術 ( ID:gh_8b69cca044dc )

Unity技術美術QQ交流分享羣:19470667

推薦閱讀:

相關文章