//
动态模糊或运动模糊(motion blur)是静态场景或一系列的图片像电影或是动画中快速移动的物体造成明显的模糊拖动痕迹。
摄影技术
当相机拍出影像时,不单只表现出单一时间的即时影像。由于技术限制或艺术要求,影像表现出的是一段时间内的场景。当场景中的物体移动时,该场景的影像必定会表现那些物体在曝光时间(取决于快门速度)内所有位置的完整组合,以及摄影机的视角。在这样的图片中,任何沿着相机相对方向运动的物体将会看起来模糊或被晃动。这种模糊可能发生在拍摄移动中的物体或拍摄静态场景而相机移动时。影片或电视的影像中看起来很自然,是因为人眼也以同样的方式运作。 [1]
由于动态模糊是相机、物体、背景间相对运动而造成的效果,可借着移动镜头追踪移动的物体来避免。因此,即使在长时间曝光下,背景比较模糊而物体看起来仍然清晰。
电脑动画
类似地,即时电脑动画中准确地显示瞬时的画面(像是一台有无限快快门的相机),而没有动态模糊。因此每秒25-30张画面的电玩游戏看起来断断续续不自然,而用相同画面更新率拍摄自然动作看起来却是连续的。很多新世代电玩游戏以动态模糊为特色,尤其是赛车模拟游戏。预先运算生成的电脑图像中,如电脑合成影像CGI的电影,可以绘出逼真的动态模糊,因为渲染器(renderer)有更多时间来绘制每个画面。时间上避免失真需依照许多瞬间时间的组合来制造画面。
图2:动态模糊经常被使用在运动(尤其是摩托车运动)的摄影上以表现出速度感。为了达到效果,必须使用慢快门并且让相机镜头随着物体的运动慢慢地转向
电子游戏
在大多数电子游戏中,都提供动态模糊特效。如果开启动态模糊,连续画面看起来将会相对平滑和顺畅(但帧数其实没有改变),在动作场景和快速运动的场景中还能获得更加真实的体验——例如飙车。
Photoshop中的应用
Photoshop中的滤镜菜单下有“动态模糊”这一操作,可以将照片处理成高速运动下被摄影机拍下的效果。
构建模型
影响模糊的参数
1 角度
0度~360度,不同角度会造成不同的模糊类型。
2 长度
长度为移动多少间隔的像素。
图4:这些城市灯光是固定的而相机的方向在曝光期间改变。虚线是由于相机来回移动造成的每盏灯的个别轨迹。
动态模糊的类型
1 线性
动态模糊发生在单一方向的模糊。镜头的角度会影响动态模糊线条的角度;滤镜的半径长度会影响模糊强度,长度越长会越模糊。
2 旋转
产生像物体在旋转的环状动态模糊。此类型的模糊以镜头对准的点为中心,角度为主要影响因素,角度变化越大越模糊。
3 缩放
缩放型的动态模糊是以图片中心为中心辐射开来的模糊。图片中心未发生模糊但以图片中心为准的外围却会模糊,会让人有凸显图片中心的动态感觉。镜头的缩放长度为主要影响因素。
图5:卡车和相机在曝光期间皆移动。因为移动相机或摇动镜头来固定指向卡车驾驶座旁的门,所以地面看起像往卡车后方移动。
数学模型
-
模糊
-
动态模糊
负面效果
电视运动转播中,一般相机每秒曝光照片25或30次,动态模糊反而变成困扰,因为在慢动作时会使抛射体或运动员的确切位置混淆。因此特殊相机常用来消除动态模糊,能在1/1000秒级数快速曝光,然后在之后的连续1/25或1/30秒传送。虽然能产生更清晰的慢动作重播,但在正常速度播放下看起来会很怪,因为眼睛预期看到动态模糊却没看到。
人眼习惯看到动态模糊,因此习惯电视或电影中的运动模糊甚至不会注意到。电脑产生的影像在时间上很分明,没有动态模糊而使动态效果降低,看起来不逼真。
有时候可用反卷积(反折积)(deconvolution)从图片中移除动态模糊。
///
Bitty
http://parnham.future.easyspace.com/
Bitty是一个小程序,可以进行空间和时间反失真(运动模糊)。输入渲染好的帧,就可以得到运动模糊的效果。它也能把图象重新采样。它不能输出动画文件,只能输出帧画面文件用于生成.MPG、.AVI或.BIK(bink!) 视频文件。
Terragen Motion Blurred Animations
http://parnham.future.easyspace.com/animations.htm
Dan通过使用Terragen渲染帧,Bitty混合帧已经创建了许多极棒的动画。
Bink!
http://www.smacker.com/ (译者注:《星际争霸》用的就是它的前身)
Bink是一种新颖出色的视频压缩解压格式。easily outperforming MPEG-IIand AVI. 它是一个很新的系统,并不象.MPG 或.AVI一样被支持,但我的确希望它能得到它应该得到的认可。从现在开始,我将用这种格式创建我所有的动画。
//
此篇介绍最简单的全局Motion Blur。算法是将当前帧与前一帧按某一比例混合。这是一个过程,例如有10帧,在第1帧中,只有第1帧原图,第2帧中有1、2帧原图,第3帧中会有1、2、3帧原图,依次类推。
假设混合比较为a,即原图为1-a,累积图为a,那么1帧新进入的图,在下一帧中,所占的比例就只有a,再下一帧就只有a^2,再下一帧就只有a^3,依次类推,第N帧后,比例会接近于0。a越小,消失的会越快,也就意味着MotionBlur效果越弱。相反,a越大,MotionBlur效果越强。通过将此混合参数叫做BlurAmount。为了防止完全抛弃新进帧的情况,通常会限制BlurAmount不大于0.92。
最后,MotionBlur的Shader需要特殊处理一下。首先需要将原始帧与累积帧混合在一起。但普通的混合会连带alpha一起混合,而我们想做做的只是混合RGB,希望alpha保留。所以需要2个Pass,第一个Pass根据BlurAmount混合RGB,第二个Pass直接写入新的alpha。完整Shader如下:
Shader "Hidden/MotionBlur" {
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {}
_AccumOrig("AccumOrig", Float) = 0.65
}
SubShader {
ZTest Always Cull Off ZWrite Off
Fog { Mode off }
Pass {
Blend SrcAlpha OneMinusSrcAlpha
ColorMask RGB
BindChannels {
Bind "vertex", vertex
Bind "texcoord", texcoord
}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma fragmentoption ARB_precision_hint_fastest
#include "UnityCG.cginc"
struct appdata_t {
float4 vertex : POSITION;
float2 texcoord : TEXCOORD;
};
struct v2f {
float4 vertex : POSITION;
float2 texcoord : TEXCOORD;
};
float4 _MainTex_ST;
float _AccumOrig;
v2f vert (appdata_t v)
{
v2f o;
o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
o.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);
return o;
}
sampler2D _MainTex;
half4 frag (v2f i) : COLOR
{
return half4(tex2D(_MainTex, i.texcoord).rgb, _AccumOrig );
}
ENDCG
}
Pass {
Blend One Zero
ColorMask A
BindChannels {
Bind "vertex", vertex
Bind "texcoord", texcoord
}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma fragmentoption ARB_precision_hint_fastest
#include "UnityCG.cginc"
struct appdata_t {
float4 vertex : POSITION;
float2 texcoord : TEXCOORD;
};
struct v2f {
float4 vertex : POSITION;
float2 texcoord : TEXCOORD;
};
float4 _MainTex_ST;
v2f vert (appdata_t v)
{
v2f o;
o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
o.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);
return o;
}
sampler2D _MainTex;
half4 frag (v2f i) : COLOR
{
return tex2D(_MainTex, i.texcoord);
}
ENDCG
}
}
SubShader {
ZTest Always Cull Off ZWrite Off
Fog { Mode off }
Pass {
Blend SrcAlpha OneMinusSrcAlpha
ColorMask RGB
SetTexture [_MainTex] {
ConstantColor (0,0,0,[_AccumOrig])
Combine texture, constant
}
}
Pass {
Blend One Zero
ColorMask A
SetTexture [_MainTex] {
Combine texture
}
}
}
Fallback off
}
///
运动模糊分为像机运动模糊和物体运动模糊,这里只讨论物体运动模糊。
一,物理正确的运动模糊
物理正确的motion blur,是两边虚中间实,可以看作是物体的一堆虚影的叠加,因为中间叠的层数多,所以最实。
另外值得注意的是,在motion blur下,物体除向外部扩散外,内部也会有一部分变成半透:
做motion blur最直观的方法就累积法:把物体以半透明形式往RT上累积,再将RT与场景合成。此法虽然直观正确但渲染流程无法接受,我们希望用后处理实现motion blur。
二,spread vs gather
用后处理实现motion blur。
若用compute shader实现,可用spread方式:将物体颜色按motion vector扩散到背景上。
但用fragment shader实现,需用gather方式:对于当前背景像素p,想知哪些物体像素能spread到它上面,为此对motion vector进行max降采样,用max motion vector作为搜索范围,认为p点max motion vector范围内的点可能spread到p点上,即check此范围内各点q,看q沿q的motion vector是否能抅到点p。
三,内模糊、外模糊、背景重建
内模糊:当前物体像素与能spread到它的物体像素取加权平均,并透出背景。
外模糊:当前背景像素与能spread到它的物体像素取加权平均。
背景重建:由于color buffer上物体像素对应的背景像素被物体挡住了,所以不可能实现“透出背景”的效果,即正确的内模糊不可能实现,只能通过物体外的背景像素去估计(重建)物体内的背景像素。
内模糊计算示例:
blur窗口为7,current pixel为物体像素,能spread到它的物体像素有四个(包括它自己),剩下7-4=3份应该取它所覆盖的背景像素颜色,但背景像素被挡住了,所以取物体外三个背景像素去冒充,所以最后7个像素权重都取1,即最后current pixel颜色=(1白+1白+1白+1黑+1黑+1黑+1黑)/7
外模糊计算示例:
blur窗口为7,current pixel是背景像素,物体内有3个像素能spread到它上面,剩余7-3=4份取它自己,所以最后current pixel颜色=(0白+0白+0白+4白+1黑+1黑+1黑)/7
之所以4份白全取current pixel自身,是因为motion blur中背景应该是清晰的,不应掺入其它背景像素颜色。
其它外模糊算例:
五,消除边界
用上面算法得到的运动模糊,外模糊是完全正确的,但内模糊由于掺入了背景重建这一步,导致内部透出来的背景是错误且模糊的。外模糊透出的背景清晰,内模糊透出的背景模糊,从而产生断层:
为了解决这个问题,采取的思路是让接近边缘处外模糊透出的背景也变模糊,从而衔接上:
具体方法是对权重做mirror:以current pixel为中心,将物体那一侧的权重1对称到背景一侧,并调低current pixel权重使权重和仍保持为blur窗口长度(此处为7)。下图左为mirror前权重,右为mirror后权重:
/
动态模糊近几年广泛应用于游戏制作的一种特效,可以使得游戏所呈现出的运动画面更接近于真实相机所拍摄出的效果。
在真实世界中,运动模糊是指在相机拍摄画面时,由于被拍摄物体在相机快门曝光的短暂时间内有一定幅度的运动,造成拍摄出的画面产生残影和模糊的效果,通常相机只有在捕捉高速运动物体或者相机本身处在高速旋转中会出现这种效果。
那么如何能利用GPU在游戏画面中实现这一效果的呢?
其实原理和实现都非常的简单,实现Motion blur的方法也有很多,这里以DirectX9自带Sample中的方法为例进行说明。
我们知道在游戏中对于画面的渲染是一种单帧渲染,通常情况下,两帧之间并没有直接关联,但是对于动态模糊在时间上是一个连续行为,最终的效果势必需要前后状态的参与。
所以实现的基本原理就是通过记录图像中每一个像素点的运动速度向量,并在该方向采样来混合绘制颜色。
就这么简单……
不过实现的时候还是有很多地方需要注意的,首先效果的实现用到了Post-Processing的概念,也就是说效果的生成并非是在每帧绘制过程中实现的,而是在当前帧绘制完成后进行的,不同的环境中可以用不同的方法完成,在DX下是通过前一文中的Render Target来实现的。
在Sample中记录每一像素点的速度是通过下一文中介绍的Multiple Render Target实现的,格式使用的是D3DFMT_A8R8G8B8和D3DFMT_G16R16F浮点型。
像素点速度的计算是通过相邻两帧之间像素的经过投影变换的世界矩阵相减得到的,不要忘记的是,坐标要除以w得到非齐次坐标后再进行相减计算。
得到像素点速度后,通过在速度方向上进行一定数量的采样,混合后即可得到该点的正确颜色。
这种方法的实现优点在于原理简单,缺点在于对于模拟极端情况下(超长曝光时间,超高运动速度)和运动轨迹弧度较大是失真比较明显。
///
///
/