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;

弱化蓝色

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