日出日落 彩虹

《the Book of Shader》学习记录01

Posted by Saniac on July 14, 2018

这两天发现了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) 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色轮 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,轮廓会很生硬。 下面是我改的效果。