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;

    弱化蓝色

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值