这两天发现了The Book Of Shader 这篇教程,开启了新世界的大门。把学习的收获记录在这。
首先学会的是用GPU处理像素这种思想,之前做动画渲染canvas一般都是写个循环,逐个像素赋RGB值这样,在glsl里,这个过程变成了一下子给所有的像素赋值。 教程里是这样比喻的:CPU处理就像一个流水线里的大管子,处理能力强,但是如果让他来逐个像素地算计算,面对800*600的屏幕,甚至2880*1800的高分屏,还是会卡住,GPU就像是很多高并发的小管子,虽然每个小管子处理能力不是很强,但处理一个单一像素的简单运算还是绰绰有余。
homework 6.1
Compose a gradient that resembles a William Turner sunset
#ifdef GL_ES
precision mediump float;
#endif
#define PI 3.14159265359
uniform vec2 u_resolution;
vec3 colorA = vec3(0.149,0.141,0.912);
vec3 colorB = vec3(1.000,0.833,0.224);
float impulse( float k, float x ){
float h = k*x/2.0;
return h*exp(1.0-h) * 0.5 + 0.2;
}
void main() {
vec2 st = gl_FragCoord.xy/u_resolution.xy;
vec3 color = vec3(0.0);
vec3 pct = vec3(st.x);
pct.b = impulse(12.0,st.x);
color = mix(colorB, colorA, smoothstep(0.7,pct.b, st.y));
color = mix(colorA, color, smoothstep(1.0,0.3*st.x +0.7,st.y));
gl_FragColor = vec4(color,1.0);
}
虽然画的有点鬼畜,但是道理还是这个道理,主要是学会怎么让自己想要的颜色呆在自己期望的位置上。
William Turner - The Fighting Temeraire (1838) 图片来自 thebookofshaders.com
homework 6.2
Animate a transition between a sunrise and sunset using u_time.
#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 sunSet(float x) {
return sin(x + u_time);
}
void main() {
vec2 st = gl_FragCoord.xy/u_resolution.xy;
vec3 color = vec3(0.0);
vec3 pct = vec3(st.x);
pct.b = sunSet(st.x);
color = mix(colorB, colorA, smoothstep(1.024,pct.b, st.y));
gl_FragColor = vec4(color,1.0);
}
注意这里smoothstep(1.024,pct.b, st.y)
,第一个参数要去比1稍大一点的,不然波峰的地方会有个缝。我理解是因为smoothstep起始值和终止值太接近了就会返回0。
homework 6.3
Can you make a rainbow using what we have learned so far?
precision highp float;
#define PI 3.14159265359
uniform vec2 u_resolution;
vec3 colorA = vec3(0.912,0.061,0.017);
vec3 colorB = vec3(1.000,0.974,0.922);
float sunSet(float x, float b) {
return sin(x * PI) / 3.0 + b;
}
void main() {
vec2 st = gl_FragCoord.xy/u_resolution.xy;
vec3 color = vec3(0.0);
float p = sunSet(st.x, 0.5);
color = mix(colorB, colorA, smoothstep(0.888,p, st.y));
color = mix(color, vec3(1.000,0.825,0.272), smoothstep(0.888, p - 0.086, st.y));
color = mix(color, vec3(0.001,1.000,0.455), smoothstep(0.752, p - 0.220, st.y));
color = mix(color, vec3(0.016,0.658,1.000), smoothstep(0.648, p - 0.428, st.y));
color = mix(color, vec3(0.016,0.658,1.000), smoothstep(0.720, p - 0.292, st.y));
color = mix(color, vec3(0.517,0.148,1.000), smoothstep(0.696, p - 0.244, st.y));
color = mix(color, vec3(0.978,0.990,1.000), smoothstep(0.656, p - 0.356, st.y));
gl_FragColor = vec4(color,1.0);
}
硬叠了个彩虹出来。。。
homework 6.4
Modify the polar example to get a spinning color wheel, just like the waiting mouse icon.
If you look closely at the color wheel used on color pickers (see the image below), they use a different spectrum according to RYB color space. For example, the opposite color of red should be green, but in our example it is cyan. Can you find a way to fix that in order to look exactly like the following image? [Hint: this is a great moment to use shaping functions.]
ryb色轮长这样,图片来自 thebookofshaders.com
#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);
}
float circle(float r, vec2 st) {
return smoothstep(r,r+0.008,distance(vec2(0.5), st));
}
float adjustment (float f) {
return pow(f, 1.416);
}
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)/TWO_PI + 0.5);
float angleplus = adjustment(angle);
float radius = length(toCenter)*2.0;
color = hsb2rgb(vec3(angleplus,radius,1.0));
color = mix(color, vec3(1.0), circle(0.476, st));
gl_FragColor = vec4(color,1.0);
}
这两个放在一起做了,注意前一题中变成色盘时,如果不加smoothstep
,轮廓会很生硬。
下面是我改的效果。