【零基礎】充分理解WebGL(六)

語言: CN / TW / HK

接上篇:http://juejin.cn/post/7108674503101661197

顏色

在前面幾篇的內容裡,我們的例子大部分都是黑白的,那是因為我們著重於渲染形狀,基本沒有考慮渲染成不同的顏色。

我們既然把shader中文翻譯成“著色器”,顧名思義,“著色器”本身就是用來在畫布上“著色”的,自然和顏色有著密切的關係。

話不多說,我們仍然通過程式碼由淺入深地來看一看。

我們已經知道了在片段著色器中,FragColor輸出顏色,它預設採用的是RGBA色值。

```glsl

version 300 es

precision highp float; out vec4 FragColor; uniform vec2 resolution;

void main() { FragColor.rgb = vec3(1.0, 0, 0); FragColor.a = 1.0; } ```

我們在第一講中,就見過,上面的程式碼將整個canvas畫布渲染為紅色。

順便說一下,碼上掘金支援了自定義語言功能,所以從這一講開始,我利用這個功能來寫shader,因為我們講WebGL的重點是渲染管線裡的著色器,所以shader程式碼是核心,利用自定義語言來寫,能夠讓讀者瀏覽語法高亮的shader程式碼,便於閱讀和修改除錯。而其他JS部分,我放在Markup的script標籤裡,不作為重點。你只要切換碼上掘金左側視窗的script標籤,就能看到語法高亮的shader程式碼了。

http://code.juejin.cn/pen/7111156081786617893

同樣,在第一講中,我們還寫過一個例子,通過座標值來繪製漸變顏色:

```glsl

version 300 es

precision highp float; out vec4 FragColor; uniform vec2 resolution;

void main() { vec2 st = gl_FragCoord.xy / resolution; FragColor.rgb = vec3(st, 0); FragColor.a = 1.0; } ```

http://code.juejin.cn/pen/7111162916430151711

我們可以利用前面學過的距離場來對顏色造型:

```glsl

version 300 es

precision highp float;

pragma include

out vec4 FragColor; uniform vec2 dd_resolution; uniform float dd_time;

void main() { vec2 st = gl_FragCoord.xy / dd_resolution; st = mix(vec2(-5), vec2(5), st); st = polar(st, vec2(0)); st = fract(st); FragColor.rgb = vec3(st.x, st.y + 0.5 * sin(dd_time), 0); FragColor.a = 1.0; } ```

http://code.juejin.cn/pen/7111163920278093838

在上面的程式碼裡,我悄悄換了一個庫,把 gl_render 換成了 glsl_doodle

它們使用上沒有太大差別,glsl_doodle 是繼承了 gl_renderer,在 gl_renderer 的基礎上支援了 #pragma include指令載入函式庫的功能。通過這個功能,我們內建了一些模組,使用它們的方法就是通過#pragma include載入進來,這和使用C語言進行載入模組非常類似。實際上很大一部分模組我們在前面的章節中都介紹了它們的具體實現。在後續的章節中我們直接將它們載入進來,這樣能保持glsl程式碼的清晰,便於理解。對於未使用過的函式,如有涉及,我再單獨介紹。

另外glsl_doodle還內建了一些uniforms,這樣我們就不用在JS中去重複定義它們,內建uniforms在命名上以dd_開頭,例如上面程式碼用到的dd_resolution,它和我們前面章節自己定義的resolution沒有區別。

漸變造型

在顏色漸變的基礎上,我們可以通過造型函式來控制漸變過程,比如下面的程式碼用三階貝塞爾曲線來控制漸變過程:

```glsl

version 300 es

precision highp float;

pragma include

pragma include

pragma include

out vec4 FragColor; uniform vec2 dd_resolution; uniform float dd_time; uniform vec3 fromColor; uniform vec3 toColor; uniform vec4 bezierController;

float cubic_bezier(float x, vec4 p) { return cubic_bezier(x, p.x, p.y, p.z, p.w); }

float cubic_bezier(float x) { return cubic_bezier(x, bezierController); }

void main() { vec2 st = gl_FragCoord.xy / dd_resolution; float d = cubic_bezier(st.x); FragColor.rgb = mix(fromColor, toColor, d); float d2 = PLOT(cubic_bezier, st, 0.01); FragColor.rgb += stroke(d2, 0.01, 0.2); FragColor.a = 1.0; } ```

http://code.juejin.cn/pen/7111175676136259621

像前面章節的例子一樣,我們也可以把直角座標對映成極座標,做出有趣的效果來:

http://code.juejin.cn/pen/7111187775952519181

HSB

就像CSS中有RGB和HSL一樣,我們可以將RGB設定轉換成極座標方式,將紅、綠、藍三原色通道對映成色相飽和度亮度三個通道,這就是HSB設定。

HSB和RGB的相互轉換公式如下:

```glsl vec3 rgb2hsb(vec3 c){ vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g)); vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r)); float d = q.x - min(q.w, q.y); float e = 1.0e-10; return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); }

// Function from Iñigo Quiles // http://www.shadertoy.com/view/MsS3Wc vec3 hsb2rgb(vec3 c){ vec3 rgb = clamp(abs(mod(c.x*6.0+vec3(0.0,4.0,2.0), 6.0)-3.0)-1.0, 0.0, 1.0); rgb = rgb * rgb * (3.0 - 2.0 * rgb); return c.z * mix(vec3(1.0), rgb, c.y); } ```

有了HSB,我們可以方便做連續的色相漸變,比如我們將上面的例子修改一下:

http://code.juejin.cn/pen/7111193630966022181

簡單運用極座標變換,我們可以看一下HSB色輪,類似CSS的HSL色輪:

```

version 300 es

precision highp float;

define WEBGL2;

pragma include

pragma include

pragma include

uniform vec2 dd_resolution; uniform float dd_time; uniform float fromColor; uniform float toColor; uniform vec4 bezierController;

void main() { vec2 st = gl_FragCoord.xy / dd_resolution; st = polar(st); FragColor.rgb = hsb2rgb(vec3(0.5 * st.y / PI +0.5,st.x,1.0)); FragColor.a = 1.0; } ```

http://code.juejin.cn/pen/7111195901342777374

掌握了顏色,我們可以來繪製一些更炫酷的東西了,不過別急,大家先通過練習掌握基礎,在後續章節裡我們再慢慢由淺入深。