“ PK创意闹新春,我正在参加「春节创意投稿大赛」,详情请看:春节创意投稿大赛”
ShaderJoy —— Shader 特效乐趣无穷
效果图
动态图
静态图
核心代码分析
话不多说,先看核心代码,我们从简单的二维坐标入手(之后再推广到三维),如下
/// @note 将屏幕坐标换算为 [-.5, .5] 的范围
vec2 p = fragCoord.xy / iResolution.xy - .5;
float pa, a = pa = 0.;
/// @note 迭代绘制亮点【分形】
for (int i = 0; i < iterations; i++)
{
/// @note 核心代码
/// 魔法公式
p = abs(p) / dot(p, p) - formuparam; ///< 迭代更新 p 坐标【随着迭代的增加会产生许多对称的点】
/// pa 是 “鼓包” 的半径,也是上一次迭代 p 坐标到原点的距离
a += abs(length(p) - pa); ///< 累加 “鼓包”
pa = length(p); ///< 更新 pa 【“鼓包” 半径也越来越小】
}
fragColor = vec4(a * 3e-3);
///fragColor = vec4(length(a * a * a * 3e-5) * ratio);
复制代码
其中最关键的就是魔法公式,即 p
坐标的迭代
p.x 的可视化
为了方便大家更形象的理解,我已经绘制好了前三次(之后的类似)迭代的 p.x
分量的函数示意图
第一次迭代
第二次迭代
第三次迭代
从以上三张图我们可以看出, p.x
从原来的 [-.5, .5] 的线性范围分布变成了非线性但对称的范围分布【p.y
同理,但分布的方向不同】
同时可视化 x 和 y 分量
现在我们进一步来同时可视化 p.x
和p.y
第一次迭代
第二次迭代
第三次迭代
……
随着迭代数的增加,将越来越有分形那味儿了
第十三次迭代
“鼓包” 的绘制
所谓 “鼓包” 其实就是 “星云” 中的小亮点,其绘制代码为
abs(length(p) - radius);
复制代码
函数示意图如下所示
radius = 0.75
radius = 0.5
其他我就不多举例了,显然是随着 radius 的变小,“鼓包” 的半径约逐渐变小
此时,我们将 “鼓包” 的累加亮度 a
进行可视化
是不是已经有了 “星云” 那味儿呢 !我们只要进一步把它推广到三维空间,进行分层(同时伴随着时间的运动)的分形叠加就可以实现 “吉星高照” 的效果了 !
静态图
动态图
最后再和我提前制作的一张 “虎年鸿运” 的图片进行混合,这样文章开头的 “吉星高照 虎年鸿运” 的效果就制作完毕了 !
完整 glsl 代码如下:
#define iterations 13
#define formuparam 0.76
#define volsteps 9
#define stepsize 0.1
#define zoom 0.800
#define float tile 0.4
#define speed 0.010
#define brightness 0.0015
#define darkmatter 0.300
#define distfading 0.37
#define saturation 0.850
#iChannel0 "file://../../images/Main_180.png"
#define ratio = 0.042
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
/// @note 准备 uv 坐标和偏移方向
vec2 texUV = fragCoord.xy / iResolution.xy;
vec2 uv = fragCoord.xy / iResolution.xy - .5;
uv.y *= iResolution.y / iResolution.x;
vec3 dir = vec3(uv * zoom, 1.); ///< 控制各(星星)层在方向上的偏移
float time = iTime * speed + .25;
vec3 from = vec3(1., .5, 0.5); ///< 人为设定的起点(三维)
from += vec3(time * 2., time, -2.); ///< 随着时间偏移
///@ note 体渲染部分
float s = 0.1;
vec3 v = vec3(0.);
for (int r = 0; r < volsteps; r++) ///< (通过 s)层层递进绘制
{
vec3 p = from + s * dir * .5;
/// @note 划分为重复的区间
p = abs(vec3(tile) - mod(p, vec3(tile * 2.)));
float pa, a = pa = 0.;
/// @note 绘制亮点
for (int i = 0; i < iterations; i++)
{
p = abs(p) / dot(p, p) - formuparam; ///< 关键迭代
/// @note pa 是半径,也是上一次迭代 p 到原点的距离
a += abs(length(p) - pa); ///< 累加
pa = length(p);
}
a = a * a * a; ///< 增加对比度
v += vec3(a * 3e-5); ///< 各层的亮度累加
s += stepsize;
}
vec3 color = texture(iChannel0, texUV).rgb;
fragColor = vec4(color + length(v) * ratio, 1.);
}
复制代码
补充
/// @note 将 p 的坐标范围划分为重复的区间
p = abs(vec3(tile) - mod(p, vec3(tile * 2.))); // tiling fold
复制代码
其函数示意图如下所示