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;
弱化蓝色