Shader 运动模糊(Motion Blur)

本文探讨了运动模糊的概念,分析了其在图像处理中的应用。通过对比单一方向高斯模糊和真实运动模糊,展示了如何利用Shader让画面自然运动,解决任意方向的运动模糊问题,并通过实例介绍了实现方法和关键步骤,包括时间依赖的模糊调整和方向计算。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

了解完高斯模糊之后,接下来看看运动模糊。

什么是运动模糊? 根据百科的定义:动态模糊或运动模糊是静态场景或一系列的图片像电影或是动画中快速移动的物体造成明显的模糊拖动痕迹。

为什么会出现运动模糊? 摄影机的工作原理是在很短的时间里把场景在胶片上曝光。场景中的光线投射在胶片上,引起化学反应,最终产生图片。这就是曝光。如果在曝光的过程中,场景发生变化,则就会产生模糊的画面。


问题一:运动模糊是否就是单一方向的高斯模糊?

我们根据运动模糊的物理成像原理可以知道,离快门关闭越近的图像越清晰,残影是存在透明度变化的,它也受速度影响:

我们尝试一下通过单一方向的高斯模糊来模拟运动模糊,看看效果如何:

// 只展示核心代码
vec4 blur13(sampler2D image, vec2 uv, vec2 resolution, vec2 direction) {
  vec4 color = vec4(0.0);
  vec2 off1 = vec2(1.411764705882353) * direction;
  vec2 off2 = vec2(3.2941176470588234) * direction;
  vec2 off3 = vec2(5.176470588235294) * direction;
  color += texture2D(image, uv) * 0.1964825501511404;
  color += texture2D(image, uv + (off1 / resolution)) * 0.2969069646728344;
  color += texture2D(image, uv - (off1 / resolution)) * 0.2969069646728344;
  color += texture2D(image, uv + (off2 / resolution)) * 0.09447039785044732;
  color += texture2D(image, uv - (off2 / resolution)) * 0.09447039785044732;
  color += texture2D(image, uv + (off3 / resolution)) * 0.010381362401148057;
  color += texture2D(image, uv - (off3 / resolution)) * 0.010381362401148057;
  return color;
}

void main() {
    gl_FragColor = blur13(texture, st, iResolution, vec2(0., 5.));
}
复制代码

虽然单一方向的高斯模糊并不完全符合运动模糊的定义。但单纯看效果其实分辨不太出来,我们再把效果强化:

问题二:如何让画面自然地动起来?

运动模糊,自然需要运动才能体现出来。首先我们实现一个简单的位移:

// 只展示核心代码
vec4 blur13(sampler2D image, vec2 uv, vec2 resolution, vec2 direction) {
  vec4 color = vec4(0.0);
  vec2 off1 = vec2(1.411764705882353) * direction;
  vec2 off2 = vec2(3.2941176470588234) * direction;
  vec2 off3 = vec2(5.176470588235294) * direction;
  color += texture2D(image, uv) * 0.1964825501511404;
  color += texture2D(image, uv + (off1 / resolution)) * 0.2969069646728344;
  color += texture2D(image, uv - (off1 / resolution)) * 0.2969069646728344;
  color += texture2D(image, uv + (off2 / resolution)) * 0.09447039785044732;
  color += texture2D(image, uv - (off2 / resolution)) * 0.09447039785044732;
  color += texture2D(image, uv + (off3 / resolution)) * 0.010381362401148057;
  color += texture2D(image, uv - (off3 / resolution)) * 0.010381362401148057;
  return color;
}

void main() {
    st += time*3.;         // time: 0~1
    st = fract(st);
    gl_FragColor = blur13(texture, st, iResolution, vec2(0., 20.));
}
复制代码

OK,有点辣眼睛。首先需要解决的问题是图像边界连接到地方并没有运动模糊:

这意味着我们要实时的取当前的坐标来对图像进行取样,所以传入一个新的参数,表示当前运动的距离:

vec4 blur13(sampler2D image, vec2 uv, vec2 resolution, vec2 direction, vec2 speed) {
  vec4 color = vec4(0.0);
  vec2 off1 = vec2(1.411764705882353) * direction;
  vec2 off2 = vec2(3.2941176470588234) * direction;
  vec2 off3 = vec2(5.176470588235294) * direction;
  color += texture2D(image, fract(uv + speed)) * 0.1964825501511404;
  color += texture2D(image, fract(uv + (off1 / resolution) + speed)) * 0.2969069646728344;
  color += texture2D(image, fract(uv - (off1 / resolution) + speed)) * 0.2969069646728344;
  color += texture2D(image, fract(uv + (off2 / resolution) + speed)) * 0.09447039785044732;
  color += texture2D(image, fract(uv - (off2 / resolution) + speed)) * 0.09447039785044732;
  color += texture2D(image, fract(uv + (off3 / resolution) + speed)) * 0.010381362401148057;
  color += texture2D(image, fract(uv - (off3 / resolution) + speed)) * 0.010381362401148057;
  return color;
}

void main() {
    vec2 speed = vec2(0, time*3.);

    gl_FragColor = blur13(inputImageTexture, myst, iResolution, vec2(0., 20.), speed);
}
复制代码

那解决完边界问题,再分析下运动模糊出现的时机,运动开始和结束肯定不会产生模糊,只有中间过程才会有模糊,所以我们根据时间来调整模糊:

我们先构造一个从 0~1 的单位时间内,它的值从 0~1~0 的变化曲线,作为我们模糊的乘数,通过这个工具,对之前的正态分布概率密度函数进行一点改造:

vec4 blur13(sampler2D image, vec2 uv, vec2 resolution, vec2 direction, vec2 speed) {
  vec4 color = vec4(0.0);
  vec2 off1 = vec2(1.411764705882353) * direction;
  vec2 off2 = vec2(3.2941176470588234) * direction;
  vec2 off3 = vec2(5.176470588235294) * direction;
  color += texture2D(image, fract(uv + speed)) *
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值