ShaderJoy —— 核心仅三行代码实现酷炫特效 “吉星高照 虎年鸿运” 【GLSL】

“ PK创意闹新春,我正在参加「春节创意投稿大赛」,详情请看:春节创意投稿大赛

ShaderJoy —— Shader 特效乐趣无穷

效果图

动态图 虎年.gif

静态图 shadertoy.png

核心代码分析

话不多说,先看核心代码,我们从简单的二维坐标入手(之后再推广到三维),如下

/// @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 分量的函数示意图

第一次迭代

image.png

第二次迭代

image.png

第三次迭代

image.png

从以上三张图我们可以看出, p.x 从原来的 [-.5, .5] 的线性范围分布变成了非线性但对称的范围分布【p.y 同理,但分布的方向不同】

同时可视化 x 和 y 分量

现在我们进一步来同时可视化 p.xp.y

第一次迭代 shadertoy.png

第二次迭代 shadertoy.png

第三次迭代 shadertoy.png

……

随着迭代数的增加,将越来越有分形那味儿了

第十三次迭代 shadertoy.png

“鼓包” 的绘制

所谓 “鼓包” 其实就是 “星云” 中的小亮点,其绘制代码为

abs(length(p) - radius);
复制代码

函数示意图如下所示

radius = 0.75

image.png

radius = 0.5

image.png

其他我就不多举例了,显然是随着 radius 的变小,“鼓包” 的半径约逐渐变小

此时,我们将 “鼓包” 的累加亮度 a 进行可视化

shadertoy.png

是不是已经有了 “星云” 那味儿呢 !我们只要进一步把它推广到三维空间,进行分层(同时伴随着时间的运动)的分形叠加就可以实现 “吉星高照” 的效果了 !

静态图

shadertoy.png

动态图

虎年.gif

最后再和我提前制作的一张 “虎年鸿运” 的图片进行混合,这样文章开头的 “吉星高照 虎年鸿运” 的效果就制作完毕了 !

Main_180.png

shadertoy.png

完整 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
复制代码

其函数示意图如下所示

image.png

猜你喜欢

转载自juejin.im/post/7054515794897534984