Site Defunct
GLSL 的各種着色器效果

GLSL 很牛逼
首先,我要說的是,GLSL 很牛逼。我知道大家都知道,但是今天我就來給大家展示一哈我為了我那個作業做的着色器(們),其中有很大一部分都是從 ShaderToy 上參考的!
Vignette
Vignette 我不知道咋翻譯,他的中文是小插圖?這也太奇怪了吧…… 但是總而言之,我們是可以知道 Vignette 的作用是可以讓遠離中心的地方(屏幕邊緣)變黑,然后整個看起來就有點像 70 年代的電影一樣的東西:

這個着色器一般在靜物,或者是復古的六七十年代的東西的時候渲染起來效果很好。他的原理很簡單,就是遠離中心的就變黑:
vec2 uv = 1.0 - uv.yx;
float vig = uv.x * uv.y * 15.0; // 15.0 是 intensity vig = pow(vig, 0.2);
gl_FragColor = vec4(pixel * vig, 1.0);
其中,intensity 的作用就是讓結果曝光,如果太大會過度曝光,而太小又會太暗。在 intensity 的下一行的 0.2 可以說是擴大范圍,如果這個數字越大,暗的范圍就越大。最后,用輸出的像素和 vignette 值相乘就可以了。
ScanLine
ScanLine 就是掃描線。在老機器里面,掃描線經常是清晰可見的。現在由於科技呀,他進步了,掃描線就沒了。但是假如還是為了這個美感的話,我們很容易用現代的,數學的方法把以前的 缺陷 給渲染出來。

實現 ScanLine 的方法有很多種。我這里用我自個兒的,其實不大高效,因為用了 if 。
vec2 realUV = vec2(uv.x * resolution.x, uv.y * resolution.y); // gl_FragCoord pixel.rgb -= (
mod(realUV.y, 2.0) <= 1.0 ?
0.1 :
0.0
);
gl_FragColor = vec4(pixel, 1.0);
這個賊明顯,就是看看 realUV (gl_FragCoord) 的 y 值對 2 取余。如果是 1.0 的話,就直接把對應的像素哪一行 變暗 。
假如你喜歡的話,可以把 time 加上去,這樣掃描線就會一直往下(上)移動:
vec2 realUV = vec2(uv.x * resolution.x, uv.y * resolution.y); // gl_FragCoord pixel.rgb -= (
mod(realUV.y + ceil(time * <移速>), 2.0大專欄 GLSL 的各種着色器效果 class="p">) <= 1.0 ?
0.1 :
0.0
);
gl_FragColor = vec4(pixel, 1.0);
你想掃描線移多快,就自個兒調移速吧。我覺得 20.0 還不錯的。
實現掃描線的方法還有很多種。我找到的有 這種 ,這種 ,還有 這種是我參考的做法 。
Pixelate
Pixelate 是像素化的意思。我 ShaderToy 上沒找到,自個兒想的。如果有的話可以告訴我一哈,三克油!

我的做法其實很簡單。我把屏幕分成比分辨率更小的小塊,然后判斷要渲染的點在哪個小塊里頭,對應采樣那個小塊的左上角的那個像素。(也就是強行下降分辨率):
vec2 realUV = vec2(uv.x * resolution.x, uv.y * resolution.y); // gl_FragCoord vec2 fakeUV = floor(realUV / 8.0) * 8.0;
vec2 uv = fakeUV / resolution.xy;
vec4 pixel = texture2D(texture, uv);
可以看到,我們就是把真實坐標除了 8.0 以后,舍掉了一切的小數點,然后把他恢復成了原來的坐標。帥不?
Frosted Glass
還記得 Windows7 Aero 的酷炫毛玻璃效果嗎?現在咱們也能大概實現它的效果啦!看回來我們的頭圖:

看到了嗎?下面的對話框那里就是毛玻璃的效果。
其實毛玻璃的效果不難,就是瞎取樣。這個來自 這里 。我們來看看:
float rand(vec2 uv) {
float a = dot(uv, vec2(92.0, 80.0));
float b = dot(uv, vec2(41.0, 62.0));
float x = (sin(a) + cos(b)) * 51.0;
return fract(x);
}
void main() {
// ... 拿到 uv vec2 rnd = vec2(rand(uv), rand(uv));
uv += rnd * 0.05;
gl_FragColor = texture2D(texture, uv);
}
rand() 可以說是一個偽隨機函數吧?輸入 uv ,然后通過 uv 和兩個奇怪的向量點乘,並且把他們的正弦值和余弦值相加,然后放大 51 倍(這個可以改),最后舍棄掉整數部分,返回小數點后就可以了。回到 main 可以發現,結果被進一步減小了 - 為了不過分采樣(就是離得太遠)。這樣就能讓采樣稍稍偏離原來的位置了!
當然,除此之外,其實你可以發現,假如不乘那個 51 ,然后點乘的那兩個向量比較小的話 —— 就可以呈現出晶體的效果了:

帥不帥?快點兒去自己試試吧!
沒了
上頭就是我用的所有的着色器了。其實要模仿舊的 CRT 顯示屏的話,這里還有:
玩得開心!
