提示
教程例子都可以到下面網(wǎng)址進(jìn)行運(yùn)行瓢捉,不需要另外安裝軟件環(huán)境:
官方提供在線編寫(xiě)shader工具:https://thebookofshaders.com/edit.php
glslsandbox網(wǎng)站:http://glslsandbox.com/
shadertoy網(wǎng)站:https://www.shadertoy.com/
本文提到的關(guān)鍵字
關(guān)鍵字 | 描述 | 圖像 |
---|---|---|
mix(a,b,float x) | 混合ab顏色,根據(jù)x的值富拗,x~0偏向a睡雇,x~1偏向b |
色彩變量
以x,y,z定義顏色是不是有些奇怪?正因如此,我們有其他方法訪問(wèn)這些變量——以不同的名字燕耿。.x, .y, .z也可以被寫(xiě)作.r, .g, .b 和 .s, .t, .p。(.s, .t, .p通常被用做后面章節(jié)提到的貼圖空間坐標(biāo))你也可以通過(guò)使用索引位置[0], [1] 和 [2]來(lái)訪問(wèn)向量
vec3 red = vec3(1.0,0.0,0.0);
red.x = 1.0;
red.y = 0.0;
red.z = 0.0;
下面的代碼展示了所有訪問(wèn)相同數(shù)據(jù)的方式:
vec4 vector;
vector[0] = vector.r = vector.x = vector.s;
vector[1] = vector.g = vector.y = vector.t;
vector[2] = vector.b = vector.z = vector.p;
vector[3] = vector.a = vector.w = vector.q;
雞尾酒
GLSL中向量類型的另一大特點(diǎn)是可以用你需要的任意順序簡(jiǎn)單地投射和混合(變量)值姜胖。這種能力被(形象地)稱為:雞尾酒誉帅。
vec3 yellow, magenta, green;
// Making Yellow
yellow.rg = vec2(1.0); // 同時(shí)設(shè)置紅綠空間
yellow[2] = 0.0; // 設(shè)置藍(lán)色值為0
// Making Magenta
magenta = yellow.rbg; // 調(diào)換了藍(lán)綠空間
// Making Green
green.rgb = yellow.bgb; // 將藍(lán)色空間覆蓋到紅色空間
混合顏色
在黃藍(lán)之間漸變過(guò)渡
#ifdef GL_ES
precision mediump float;
#endif
uniform vec2 u_resolution;
uniform float u_time;
vec3 colorA = vec3(0.149,0.141,0.912);
vec3 colorB = vec3(1.000,0.833,0.224);
void main() {
vec3 color = vec3(0.0);
float pct = abs(sin(u_time));
// Mix uses pct (a value from 0-1) to
// mix the two colors
color = mix(colorA, colorB, pct);
gl_FragColor = vec4(color,1.0);
}
繪制漸變
#ifdef GL_ES
precision mediump float;
#endif
#define PI 3.14159265359
uniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_time;
vec3 colorA = vec3(0.149,0.141,0.912);
vec3 colorB = vec3(1.000,0.833,0.224);
float plot (vec2 st, float pct){
return smoothstep( pct-0.01, pct, st.y) -
smoothstep( pct, pct+0.01, st.y);
}
void main() {
vec2 st = gl_FragCoord.xy/u_resolution.xy;
vec3 color = vec3(0.0);
vec3 pct = vec3(st.x);
// pct.r = smoothstep(0.0,1.0, st.x);
// pct.g = sin(st.x*PI);
// pct.b = pow(st.x,0.5);
color = mix(colorA, colorB, pct);
// Plot transition lines for each channel
// color = mix(color,vec3(1.0,0.0,0.0),plot(st,pct.r));
// color = mix(color,vec3(0.0,1.0,0.0),plot(st,pct.g));
color = mix(color,vec3(0.0,0.0,1.0),plot(st,pct.b));
gl_FragColor = vec4(color,1.0);
}
在05講中的代碼,稍微改一下右莱,之前混合顏色是通過(guò)公式(1-ps)a+psb蚜锨,也就是用mix去簡(jiǎn)化了它的寫(xiě)法mix(a,b慢蜓,ps)
色相
不是出賣色相的色相亚再,是HSB(色相,飽和度和亮度)
將x坐標(biāo)(位置)映射到Hue值并將y坐標(biāo)映射到明度晨抡,我們就得到了五彩的可見(jiàn)光光譜针余。這樣的色彩空間分布實(shí)現(xiàn)起來(lái)非常方便饲鄙,比起RGB,用HSB來(lái)拾取顏色更直觀圆雁。
我們不能脫離色彩空間來(lái)談?wù)擃伾碳丁U缒闼藃gb值伪朽,有其他不同的方法去描述定義顏色轴咱。HSB代表色相,飽和度和亮度(或稱為值)烈涮。這更符合直覺(jué)也更有利于組織顏色朴肺。稍微花些時(shí)間閱讀下面的
rgb2hsv()
和hsv2rgb()
函數(shù)。
vec3 rgb2hsb( in 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);
}
vec3 hsb2rgb( in 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);
}
#ifdef GL_ES
precision mediump float;
#endif
uniform vec2 u_resolution;
uniform float u_time;
void main(){
vec2 st = gl_FragCoord.xy/u_resolution;
vec3 color = vec3(0.0);
// We map x (0.0 - 1.0) to the hue (0.0 - 1.0)
// And the y (0.0 - 1.0) to the brightness
color = hsb2rgb(vec3(st.x,1.0,st.y));
gl_FragColor = vec4(color,1.0);
}
極坐標(biāo)下的HSB
HSB原本是在極坐標(biāo)下產(chǎn)生的(以半徑和角度定義)而并非在笛卡爾坐標(biāo)系(基于xy定義)下坚洽。將HSB映射到極坐標(biāo)我們需要取得角度和到像素屏中點(diǎn)的距離戈稿。由此我們運(yùn)用 length() 函數(shù)和 atan(y,x) 函數(shù)(在GLSL中通常用atan(y,x))。
當(dāng)用到矢量和三角學(xué)函數(shù)時(shí)讶舰,vec2, vec3 和 vec4被當(dāng)做向量對(duì)待鞍盗,即使有時(shí)候他們代表顏色。我們開(kāi)始把顏色和向量同等的對(duì)待跳昼,事實(shí)上你會(huì)慢慢發(fā)現(xiàn)這種理念的靈活性有著相當(dāng)強(qiáng)大的用途般甲。
一旦我們得到角度和長(zhǎng)度,我們需要單位化這些值:0.0到1.0鹅颊。在27行敷存, atan(y,x) 會(huì)返回一個(gè)介于-PI到PI的弧度值(-3.14 to 3.14),所以我們要將這個(gè)返回值除以
TWO_PI
(在code頂部定義了)來(lái)得到一個(gè)-0.5到0.5的值堪伍。這樣一來(lái)锚烦,用簡(jiǎn)單的加法就可以把這個(gè)返回值最終映射到0.0到1.0。半徑會(huì)返回一個(gè)最大值0.5(因?yàn)槲覀冇?jì)算的是到視口中心的距離帝雇,而視口中心的范圍已經(jīng)被映射到0.0到1.0)涮俄,所以我們需要把這個(gè)值乘以二來(lái)得到一個(gè)0到1.0的映射。
#ifdef GL_ES
precision mediump float;
#endif
#define TWO_PI 6.28318530718
uniform vec2 u_resolution;
uniform float u_time;
// Function from I?igo Quiles
// https://www.shadertoy.com/view/MsS3Wc
vec3 hsb2rgb( in 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);
}
void main(){
vec2 st = gl_FragCoord.xy/u_resolution;
vec3 color = vec3(0.0);
// Use polar coordinates instead of cartesian
vec2 toCenter = vec2(0.5)-st;
float angle = atan(toCenter.y,toCenter.x);
float radius = length(toCenter)*2.0;
// Map the angle (-PI to PI) to the Hue (from 0 to 1)
// and the Saturation to the radius
color = hsb2rgb(vec3((angle/TWO_PI)+0.5,radius,1.0));
gl_FragColor = vec4(color,1.0);
}
如果你仔細(xì)觀察用來(lái)拾色的色輪(見(jiàn)下圖)摊求,你會(huì)發(fā)現(xiàn)它用一種根據(jù)RYB色彩空間的色譜。例如刘离,紅色的對(duì)面應(yīng)該是綠色室叉,但在我們的例子里是青色。你能找到一種修復(fù)的方式來(lái)讓它看起來(lái)和下圖一樣么硫惕?[提示:這是用塑形函數(shù)的好機(jī)會(huì)茧痕!]
函數(shù)和變量
int newFunction(in vec4 aVec4, // read-only
out vec3 aVec3, // write-only
inout int aInt); // read-write
在進(jìn)入下一章之前讓我們停下腳步回顧下。復(fù)習(xí)下之前例子的函數(shù)恼除。你會(huì)注意到變量類型之前有個(gè)限定符
in
踪旷,在這個(gè) qualifier (限定符)例子中它特指這個(gè)變量是只讀的曼氛。在之后的例子中我們會(huì)看到可以定義一個(gè)out
或者inout
變量。最后這個(gè)inout
令野,再概念上類似于參照輸入一個(gè)變量舀患,這意味著我們有可能修改一個(gè)傳入的變量。
或許你還不相信我們可以用所有這些元素來(lái)畫(huà)一些炫酷的東西气破。下一章我們會(huì)學(xué)習(xí)如何結(jié)合所有這些技巧通過(guò)融合 (blending) 空間來(lái)創(chuàng)造幾何形狀聊浅。沒(méi)錯(cuò)。现使。低匙。融合(blending) 空間。