在正文第一句加入“我正在参加「创意开发 投稿大赛」详情请看:掘金创意开发大赛来了!”
效果图
- 仅伸缩的 “+”
- 加上旋转的 “+”
算法思路
划分网格
首先,需要针对 uv
坐标划分网格
ouv
的可视化
对应代码如下
/// @note 划分格子,确保一个格子内是同一值
uv *= SIZE;
vec2 ouv = floor(uv) / SIZE;
接着,对每一小格的 uv
进行调整,使新的 uv
坐标的中心位于小格的 (0., 0.) 处
uv
的可视化
对应的代码如下
uv = fract(uv) - 0.5;
径向差分
划分好网格之后,下一步要实现的效果,需要一个 a
值,它从屏幕中心沿(圆的)半径向外的不同的格子所代表的值不同,但在同一半径的圆圈上,该值是相同的。
该值后续会搭配时间变化被进一步用作 “伸缩” 和 “旋转” 的计算。
对应的代码如下
float a = length(ouv * 5.0) - iTime * 2.5; ///< 随着时间外扩
绘制 “+”
“+” 实际是由一横一竖两条线段拼接而成的,此处分别使用了 uv.x
和 uv.y
的绝对值
对应代码如下
float mask = smoothstep(s + sm, s, abs(uv.x)); ///< 绘制 "|"
mask += smoothstep(s + sm, s, abs(uv.y)); ///< 绘制 "——"
mask
可视化如下
值得注意的是, 各个 “+” 的中心位于各个小格 uv 的 (0., 0.) 点处
PS:
smoothstep
是平滑阶梯函数,shader 里面使用十分频繁,网上资料也很多,我这里不做过多讲解,感兴趣的朋友可以去自行搜索
旋转的效果
由于旋转就是套用旋转矩阵的公式,这里就不再赘述,对应的代码如下所示
/// @note 旋转 uv
float ca = cos(a);
float sa = sin(a);
mat2 rot = mat2(ca, - sa, sa, ca);
uv *= rot;
为了看得更清楚,我将格子调整为 5*5 大小,(同时也再一次印证了 “+” 的中心位于各个小格 uv 的 (0., 0.) 点处)
伸缩的效果
这里用所用的 ca
,即我们前一个步骤所计算的 cos(a)
,通过它进一步构造了一个遮罩,来实现对 “+” 的伸缩效果。对应的代码如下
s = 0.25 + (ca * 0.5 + 0.5) * 0.2; ///< 取值范围在 [0.25, 0.45],用于控制 “+” 的缩放
mask *= smoothstep(s, s - sm, abs(uv.x));
mask *= smoothstep(s, s - sm, abs(uv.y));
介绍完了基本的算法思路,接下来我们直接面对完整代码吧(不用害怕,代码不多,而且我详细注释了)
代码与详细注释
// https://www.shadertoy.com/view/WdBSWd
#define SIZE 25.0
#define COL_BLACK vec3(23, 32, 38) / 255.0
#define COL_WHITE vec3(245, 248, 250) / 255.0
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
/// @note 等比例变换(抵消窗口的缩放对 uv 坐标的影响)
/// 等价于 (fragCoord - iResolution.xy * 0.5) / iResolution.xy * vec2(iResolution.x/iResolution.y, 1.)
/// uv 坐标取值范围是 vec2([-.5, .5] * iResolution.x/iResolution.y, [-.5, .5])
vec2 uv = (fragCoord - iResolution.xy * 0.5) / iResolution.y;
/// @note 划分格子,确保一个格子内是同一值
uv *= SIZE;
vec2 ouv = floor(uv) / SIZE;
// fragColor = vec4(ouv, 0., 1.);
// return;
/// @note 对每一小格的 uv 进行调整,使新的 uv 坐标的中心位于小格的 (0., 0.) 处
uv = fract(uv) - 0.5;
// fragColor = vec4(uv, 0., 1.);
// return;
/// @note 确保不同格子(从屏幕中心沿半径向外所呈现的)旋转角度不同
/// 同一半径上,旋转角度相同
float a = length(ouv * 5.0) - iTime * 2.5; ///< 时间的变化使其不断外扩
// fragColor = vec4(length(ouv * 5.0));
// return;
/// @note 旋转 uv
float ca = cos(a);
float sa = sin(a);
mat2 rot = mat2(ca, - sa, sa, ca);
// uv *= rot;
float sm = 1.0 / (iResolution.x / SIZE) * 2.0;
float s = 0.05;
/// @note 绘制 “+”
float mask = smoothstep(s + sm, s, abs(uv.x));
mask += smoothstep(s + sm, s, abs(uv.y));
s = 0.25 + (ca * 0.5 + 0.5) * 0.2; ///< 取值范围在 [0.25, 0.45],用于控制 “+” 的缩放
mask *= smoothstep(s, s - sm, abs(uv.x));
mask *= smoothstep(s, s - sm, abs(uv.y));
vec3 col = mix(COL_BLACK, COL_WHITE, mask);
fragColor = vec4(col, 1.0);
}