ShaderJoy —— “爽到飞起的 2233 娘” 效果 【GLSL】|8月更文挑战

723 阅读2分钟

​ 这是我参与8月更文挑战的第5天,活动详情查看: 8月更文挑战

2233 娘

  

其中关键需要用到的 iChannel0 是一幅由一系列连贯动作的 2233 娘组成的序列帧图像,如下图所示

由于我们可以提前知道设计师给定的一维序列帧纹理其中包含了多少帧的动画(2233 娘的话就是 7 帧),所以我们可以对 uv 坐标进行手动划分。 接下来主要的核心思想就是在不同的时刻能够显示出序列帧纹理 “下一帧” 的图像内容。

 GLSL 代码与详细算法注释如下:

#iChannel0 "file://../images/bilibili.png"

#iUniform vec2 off = vec2(0.33, 0.15) in {vec2(0., 0.), vec2(1., 1.)}
#iUniform float scale = 1. in {0.1, 4.}
const float FRMS = 7.;
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
    vec2 p = vec2(fragCoord.xy / iResolution.xy) ;

    // 控制 2233 娘的上升的 uv
    // vec2 uv = vec2( p.x + mod(iTime, FRMS * 10.), p.y );
    vec2 uv = vec2( p.x, p.y - mod(iTime, FRMS * 10.) );

    // 重新计算uv,比例和位置的调整
    uv = (uv - off) * vec2(scale);
    // uv = clamp( uv, 0.0, 1.0 );

    // 控制切换到下一帧, floor 很重要
    float ofx = floor( mod( iTime * 20.0, FRMS ) );

    // 换算单帧 2233 娘的大小 (uv scale)
    float ww = 1.0 / FRMS;

    // 将偏移后的 uv 换算到单只 2233 娘的位置,否则会把下一帧一并显示出来
    uv.x = ( uv.x + ofx) * ww;

    vec4 fg = texture2D( iChannel0, uv );
    // fragColor = fg; ///< 直接显示透明通道可能有问题
    // fragColor = vec4(fg.a); ///< 测试
    fragColor = vec4(mix(vec3(.9), fg.xyz, fg.a), 1.);
}

类似的,我们可以更进一步实现以下效果

彩虹猫

iChannel0 是一幅由一系列连贯动作的彩虹猫组成的序列帧图像,如下图所示

所使用的一维帧动画,这部分原理和 2233 娘的序列帧动画相同,只不过序列帧图像的帧数变为了 6 。

iChannel1 是任意一副背景图像,这也是和 2233 娘效果的最大区别 —— 多了需要与背景图片进行混合,以及用代码绘制的抖动彩虹效果。其中彩虹效果的核心思想是在不同高度即不同层的彩虹通过 sin 函数来模拟。

GLSL 代码与详细算法注释如下:

uniform float iGlobalTime;
uniform sampler2D iChannel0;
uniform sampler2D iChannel1;

vec2 iResolution = vec2(512., 512.);

void main()
{
    vec2 p = vec2(gl_FragCoord.x/ iResolution.x,  gl_FragCoord.y/ iResolution.y) ;
   // 背景滚动的uv
   vec2 uv = vec2( p.x+mod(iGlobalTime,2.0), p.y );   
   vec3 bg = vec3(0.0,51.0/255.0,102.0/255.0);
   float f = texture2D( iChannel1, uv ).x;
   f = f*f;
   bg = mix( bg, vec3(1.0), f );
   
   float a = 0.01*sin(40.0*p.x + 40.0*iGlobalTime);
   float h = (a+p.y-0.3)/(0.7-0.3);
   // 彩虹的位置
   if( p.x<0.6 && h>0.0 && h<1.0 )
   {
       // 彩虹的宽度
       h = floor( h*6.0 );
       // 在不同高度绘制彩虹
       bg = mix( bg, vec3(1.0,0.0,0.0), 1.0 - smoothstep( 0.0, 0.1, abs(h-5.0) ) );
       bg = mix( bg, vec3(1.0,0.6,0.0), 1.0 - smoothstep( 0.0, 0.1, abs(h-4.0) ) );
       bg = mix( bg, vec3(1.0,1.0,0.0), 1.0 - smoothstep( 0.0, 0.1, abs(h-3.0) ) );
       bg = mix( bg, vec3(0.2,1.0,0.0), 1.0 - smoothstep( 0.0, 0.1, abs(h-2.0) ) );
       bg = mix( bg, vec3(0.0,0.6,1.0), 1.0 - smoothstep( 0.0, 0.1, abs(h-1.0) ) );
       bg = mix( bg, vec3(0.4,0.2,1.0), 1.0 - smoothstep( 0.0, 0.1, abs(h-0.0) ) );
   }
   // 重新计算uv,比例和位置的调整
   uv = (p - vec2(0.33,0.15)) / (vec2(1.3, 1.) - vec2(0.33,0.15));  
   uv = clamp( uv, 0.0, 1.0 );
   
   // 控制彩虹猫图像的偏移, floor很重要
   float ofx = floor( mod( iGlobalTime*10.0*2.0, 6.0 ) );
   
   // 换算单只彩虹猫的纹理大小 (uv scale)
   float ww = 31.0/200.0;
   // uv.y = 1.0-uv.y;
   // 将偏移后的uv换算到单只彩虹猫的纹理大小,否则会把之后的彩虹猫一并显示出来
   uv.x = clamp( ( uv.x + ofx)*ww, 0.0, 1.0 );
   
   vec4 fg = texture2D( iChannel0, uv );
   vec3 col = mix( bg, fg.xyz, .8 );
   gl_FragColor = vec4( col, 1.0 );
}

还可以进一步引申到二维帧动画 爆炸火焰

爆炸火焰

所使用的二维帧动画