The Book of Shader - 造型函数 - 颜色

1 造型函数

这章比较简单,主要是使用Step和SmoothStep,绘制各种函数图形,

画布

首先了解画布的坐标,即st.xy, 是从左下到右上的

绘制y=x

float plot(vec2 st) {    return smoothstep(0.02, 0.0, abs(st.y - st.x));
}
​
void main(){......float pct = plot(st);color = (1.0-pct)*color+pct*vec3(0.0,1.0,0.0);
}

Step和SmoothStep

step(float edge0, float x); // 或float换为vec2~vec4

smoothstep(float edge0, float edge1, float x);

这里的smoothstep(0.02, 0.0, abs(st.y - st.x)) 是|y-x|范围在0~0.02时在0~1插值,由于不可能<0, 则只有>0.02时值取1

线条混合

color = (1.0-pct)*color+pct*nColor;

在原本pct对应的函数位置上用nColor覆盖原颜色,在pct之外保留原color颜色

绘制任意线条

 float plot(vec2 uv, float f){return smoothstep(f-0.02, f, uv.y)-smoothstep(f, f+0.02, uv.y);}

练习

  • sin(x+u_time)

 调一下频率

  • sin(PI/x)

     

  • sin(u_time*x)

        (为以下图片的模糊变化,由于做成gif太大了,没必要) 

  • sin(x)+1.0

  • 2.0*sin(x)

  • abs(sin(x))

  • fract(sin(x))

  • ceil(sin(x))

  • floor(sin(x))

2 颜色

Mix()

Mix(vec4 colA, vec4 colB, float x);

相当于Lerp,vec4可以随便切换为vec1~3

练习

  • 渐变表示落日

  • 1~0.5黄蓝渐变(最顶部的值是0.5黄+0.5蓝,最中间是1.0黄+0.0蓝)

    0.5~0.8红渐变,0.8~1红(0.5~0.8是原图到红的渐变)

       color = mix(vec4(0.0,0.0,1.0,1.0), vec4(1.0,1.0,0.0,1.0),0.5 + 0.5 * (1.0 - st.y));color = mix(color,vec4(0.8,0.0,0.0,1.0), smoothstep(0.5,0.8,1.0 - st.y));

    • 用u_time绘制日出日落动画

      第一版:效果一般,代码可读性也较差

              float skyline = 0.25 + 1.5*m_time;float redsmooth = 0.2;float whitesmooth = 0.4;float blacksmooth = 0.8;
      ​color = mix(vec4(0.4,0.4,1.0,1.0), vec4(1.0,1.0,0.0,1.0),skyline + (1.0-skyline) * (1.0 - st.y));color = mix(color, vec4(0.8,0.0,0.0,1.0), smoothstep(1.0-skyline,(1.0-skyline) + redsmooth,1.0 - st.y));color = mix(color, vec4(0.0,0.0,0.0,1.0), smoothstep((1.0-skyline) + redsmooth, (1.0-skyline) + redsmooth + blacksmooth, 1.0 - st.y));color = mix(color, vec4(1.0,1.0,1.0,1.0),smoothstep((1.0 - skyline) - 0.8,(1.0-skyline) - 0.8 - whitesmooth,1.0 - st.y));//1~2

      第二版:效果较好,调参调了一万年    

          // 画圆函数float circle(vec2 uv, vec2 center, float radius){float edge = 0.008;float up = center.y + sqrt(pow(radius, 2.0) - pow(uv.x - center.x, 2.0));float down = center.y - sqrt(pow(radius, 2.0) - pow(uv.x - center.x, 2.0));return (1.0 - smoothstep(up-edge, up, uv.y)) *smoothstep(down, down + edge, uv.y);}
      ​void main() {vec2 st = gl_FragCoord.xy/u_resolution.xy;vec2 mouse = u_mouse.xy/u_resolution.xy;float m_time = 0.5 + 0.5*sin(u_time * time_scale); // time_scale设置的0.5
      ​float skyline = 0.32; // 水天交界线位置
      ​// 颜色设置vec4 bg_col = vec4(1, 1, 1, 1);vec4 color = bg_col;
      ​// 太阳设置vec2 s_center = vec2(0.5 + 0.15 * m_time,0.8-0.68*m_time);float cir = circle(st, s_center, 0.12);vec4 sun_col = mix(vec4(1.000,0.999,0.503,1.000),vec4(1.000,0.011,0.136,1.000), m_time);
      ​// 天空设置float skyedge = 0.01;float sky = smoothstep(skyline,skyline + skyedge,st.y);vec4 skycolor_up = mix(vec4(0.624,0.774,0.985,1.000),vec4(0.135,0.135,0.135,1.000), m_time);vec4 skycolor_down = mix(vec4(1.000,0.868,0.010,1.000),vec4(1.000,0.348,0.006,1.000), m_time);
      ​// 海洋设置float sea = 1.0 - sky;vec4 sea_col =mix( mix(vec4(0.020,0.545,0.985,1.000),vec4(0.071,0.195,0.615,1.000), m_time),mix(vec4(0.000,0.985,0.891,1.000),vec4(0.022,0.703,0.830,1.000), m_time),(st.y-skyline) * 1.0/(skyline));
      ​// 反射设置vec4 ref_col = sun_col;float ref_width = 0.3 - 0.05 * m_time;float ref = sea * (smoothstep(s_center.x+ 0.5 * st.y - ref_width,s_center.x, st.x) -smoothstep(s_center.x,s_center.x-0.5*st.y + ref_width, st.x));
      ​// 颜色绘制color = (1.0 - sky)*color + sky * mix(skycolor_down, skycolor_up, (st.y-skyline) * 1.0/(1.0-skyline)); // 绘制天空color = (1.0 - cir)*color + cir * sun_col; // 绘制太阳color = (1.0 - sea)*color + sea * sea_col; // 绘制海面color = (1.0 - ref)*color + ref * ref_col; // 绘制反射
      ​gl_FragColor = color;}
      • 天空:顶部颜色到底部颜色的混合,顶部颜色和底部颜色随时间改变

      • 太阳:位置和颜色随时间变化

      • 海面:远处颜色和近处颜色的混合,远处颜色和近处颜色随时间改变

      • 反射:在海面区域以太阳位置的x值为中心,画矩形,通过控制smoothstep第1,2个参数来控制范围,越远处越窄,颜色和太阳颜色相同

        (GIF压缩后质量有限)

  • 彩虹

    • 找一个彩虹的曲线

    • 正方形的画布不好放下,换一个长方形的600*300

      renderer.setSize(600, 300);
    • 映射到0~1的uv然后调一下最高高度

      float line_a = plot(st, 0.88 * pow(cos(PI * ((st.x - 0.5) * 2.0 ) / 2.0), 0.5));

    • 同理画多几条曲线

          float line_b = plot(st, 0.68 * pow(cos(PI * ((st.x - 0.5) * 2.0 / 0.8 ) / 2.0), 0.5));float line_c = plot(st, 0.48 * pow(cos(PI * ((st.x - 0.5) * 2.0 / 0.6 ) / 2.0), 0.5));

    • 但是填色出现问题,还需要计算和线的距离来决定颜色,不如换成与(0.0,0.5)点的距离。然后用hsb

              // 归一化到0~1;float distance = pow(st.x - 0.5, 2.0) + pow(st.y, 2.0) / (0.5 * 0.5 + 1.0 * 1.0); color = vec4(hsb2rgb(vec3(distance, 1.0, 1.0)),1.0);

      换一种方式看看,这样表示更直观

               float max = (pow((1.0 - 0.5), 2.0) + pow(1.0, 2.0));float distance = (pow((st.x - 0.5), 2.0) + pow(st.y, 2.0)) / max;

      效果与上面相同

      • 设置一个边界条件截取(前面的划线步骤)

        发现一个问题

        float line_down = step(sqrt(0.1 * 0.1 - pow(st.x - 0.5, 2.0)), st.y) ;

        这句话的范围为

        由于在周围没有定义域,所以取值是0。于是添加定义域

        float line_down = step(sqrt(0.1 * 0.1 - pow(st.x - 0.5, 2.0)), st.y) + (1.0 - step(0.5 - 0.1, st.x) + step(0.5 + 0.1, st.x)) ;

        添加颜色

        下面这条线和上面那条线不平行,想个办法

        float line_up = step(sqrt(0.9 * 0.9 - pow(st.x - 0.5, 2.0)), st.y);
        float line_down = step(sqrt(0.9 * 0.9 - pow(st.x - 0.5, 2.0)) - line_width, st.y);

        颜色不对了,修改一下distance,然后加个参数调节彩虹颜色范围;

                float line_width = 0.5;float line_up = sqrt(0.9 * 0.9 - pow(st.x - 0.5, 2.0));float step_up = step(line_up, st.y);float line_down = sqrt(0.9 * 0.9 - pow(st.x - 0.5, 2.0)) - line_width;float step_down = step(line_down, st.y);float line = step_down - step_up;
        ​float rainbow_scale = 1.5;float max = line_width * rainbow_scale;float distance = (st.y - line_down)/max;

        效果不错,添加一点透明度,彩虹边界柔和一点,再加一个有趣渐变的背景

                vec4 bg_col = mix(vec4(1.000,0.728,0.806,1.000),vec4(0.611,0.490,0.930,1.000),- 0.68 + st.x +  1.0 - st.y);......float line_width = 0.5;float line_smooth = 0.05;float line_up = sqrt(0.9 * 0.9 - pow(st.x - 0.5, 2.0));float step_up = smoothstep(line_up, line_up + line_smooth, st.y);float line_down = line_up - line_width;float step_down = smoothstep(line_down - line_smooth, line_down, st.y);float line = step_down - step_up;
        ​float rainbow_scale = 1.5;float max = line_width * rainbow_scale;float distance = (st.y - line_down)/max;
        ​float alpha = 0.584;color = vec4(hsb2rgb(vec3(distance, 1.0, 1.0)),1.0);color = line * mix(color, bg_col, 1.0 - alpha) + (1.0 - line) * bg_col;gl_FragColor = color;

        完美!左上角用的是蓝色的补色,右下角用的是红色的补色,为了突出彩虹背景颜色稍微调淡一点。和彩虹的颜色配合在一起,有点迷幻的感觉,联想起彩虹小马这种题材。

        但是颜色太多还是有点杂乱,颜色越多越难把握整个画面,如果自己工程里面要做这种彩虹,最好搞得不要太突出,alpha值调低一点。

  • step彩旗

    与彩虹差不多,先找函数,自己在Graphtoy用sin玩了个函数

    6.18 + 0.04 *2.0 * (x +12)* sin((0.5 - 0.04* x) * x + (4 + 0.0005 * x)* t)

    映射到0~1的区域,转到glsl,调下参数,其他都和彩虹一样

            float line_width = 0.618;float line_smooth = 0.05;float line_up = 0.5 + line_width / 2.0 + 0.032 * st.x * sin((0.5 - 0.368 * st.x) * st.x + (4.0  + 0.005 * st.x)* u_time);float step_up = smoothstep(line_up, line_up + line_smooth, st.y);float line_down = line_up - line_width;float step_down = smoothstep(line_down - line_smooth, line_down, st.y);float line = step_down - step_up;

    效果如下

    • 越靠近旗轴浮动越小,浮动频率越慢

HSB颜色

HSB 对应着色相(hues)饱和度(saturation)亮度(brightness) ,相较于RGB更符合直观的颜色感觉

HSB对应一个圆柱,即极坐标下的RGB,具体应该是柱坐标,其中S,H是极坐标,B还是按坐标轴线性的

练习

  • 极坐标映射改为色轮

    根据例子找到个新的画圆边界的方式

    vec2 toCenter = vec2(0.5)-st;
    float cir = step(0.1, sqrt(pow(toCenter.x, 2.0) + pow(toCenter.y, 2.0)));

    圆环也很好画了,调一下参数

            // 设置背景vec4 bg_col = vec4(0.847,0.763,0.880,1.000);      vec4 color = bg_col;// 获得参数vec2 toCenter = vec2(0.5)-st;float angle = atan(toCenter.y,toCenter.x)/(2.0*PI);// 构造圆环vec4 line_col = vec4(hsb2rgb(vec3(angle,0.8,1.0)),1.0);float dis = sqrt(pow(toCenter.x, 2.0) + pow(toCenter.y, 2.0));float cir_a = smoothstep(0.309,0.305, dis);float cir_b = smoothstep(0.38,0.37, dis);float cir = cir_b - cir_a;// 赋值color = cir * line_col + (1.0 - cir)* color;

    结果如下:

  • RGB HSV转换中强调某特定值

    加强红色试试,在hsv2rgb里面加一行 rgb.r+=0.3;

    弱化蓝色


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部