动机
在shadertoy看到这个动态模糊的代码Analytical Motionblur 2D。不只是他的动态模糊,他整体的颜色、背景、小球的运动等都感觉很棒。代码也不长,因此仔细研究了下。
具体效果如下:

出于好奇尝试了100个小球、1000个小球高速运动的情况:


下面一步步实现。
1. 坐标映射
首先对原来的屏幕坐标 fragCoord 重新做映射,映射后屏幕坐标如下:

通过除以屏幕高度,将纵坐标限制在 [ − 1 , 1 ] [-1,1] [−1,1]。代码:
vec2 p = (2.0*fragCoord.xy-iResolution.xy) / iResolution.y;
2. 画背景
这个效果让人看着舒服,和他的背景关系也非常大。他是从纵向渐变的黑色,并且非常smooth。实现代码是这样的:
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
vec2 p = (2.0*fragCoord.xy-iResolution.xy) / iResolution.y;
vec3 col = vec3(0.2) + 0.05*p.y;
// 加入噪声
col += (1.0/255.0)*hash3(p.x+13.0*p.y); // hash3返回随机3维向量
fragColor = vec4(col,1.0);
}
hash3返回 [ 0 , 1 ) [0,1) [0,1)的随机三维向量。给像素点的颜色值加上这个随机数,是为了避免webGL的优化。如果去掉这行代码,最终结果如下左图,加上后如下右图。

3. 生成运动的小球
先看想要达到的效果:

首先会需要用来生成运动小球的函数:
const float speed = 5.0;
vec2 getPosition( float time, vec4 id ) {
return vec2(0.9*sin((speed*(0.75+0.5*id.z))*time+20.0*id.x),
0.75*cos(speed*(0.75+0.5*id.w)*time+20.0*id.y) );
}
不同的 i d id id能得到不同的小球,他们的运动轨迹在 x x x轴 y y y轴上的投影都是正弦函数。四维向量 i d id id的每个分量分别决定了:小球水平运动的初项、小球水平运动的频率、小球垂直运动的初项、小球垂直运动的频率。常量 s p e e d