【零基礎】充分理解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

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