/* CAMERA MOTION BLUR IMAGE EFFECTS Reconstruction Filter: Based on "Plausible Motion Blur" http://graphics.cs.williams.edu/papers/MotionBlurI3D12/ CameraMotion: Based on Alex Vlacho's technique in http://www.valvesoftware.com/publications/2008/GDC2008_PostProcessingInTheOrangeBox.pdf SimpleBlur: Straightforward sampling along velocities ScatterFromGather: Combines Reconstruction with depth of field type defocus */ Shader "Hidden/CameraMotionBlur" { Properties { _MainTex ("-", 2D) = "" {} _NoiseTex ("-", 2D) = "grey" {} _VelTex ("-", 2D) = "black" {} _NeighbourMaxTex ("-", 2D) = "black" {} _TileTexDebug ("-", 2D) = "" {} } CGINCLUDE #include "UnityCG.cginc" // 's' in paper (# of samples for reconstruction) #define NUM_SAMPLES (11) // # samples for valve style blur #define MOTION_SAMPLES (16) // 'k' in paper float _MaxRadiusOrKInPaper; static const int SmallDiscKernelSamples = 12; static const float2 SmallDiscKernel[SmallDiscKernelSamples] = { float2(-0.326212,-0.40581), float2(-0.840144,-0.07358), float2(-0.695914,0.457137), float2(-0.203345,0.620716), float2(0.96234,-0.194983), float2(0.473434,-0.480026), float2(0.519456,0.767022), float2(0.185461,-0.893124), float2(0.507431,0.064425), float2(0.89642,0.412458), float2(-0.32194,-0.932615), float2(-0.791559,-0.59771) }; struct v2f { float4 pos : SV_POSITION; float2 uv : TEXCOORD0; }; sampler2D _MainTex; sampler2D_float _CameraDepthTexture; sampler2D _VelTex; sampler2D _NeighbourMaxTex; sampler2D _NoiseTex; sampler2D _TileTexDebug; float4 _MainTex_TexelSize; float4 _CameraDepthTexture_TexelSize; float4 _VelTex_TexelSize; half4 _MainTex_ST; half4 _CameraDepthTexture_ST; half4 _VelTex_ST; float4x4 _InvViewProj; // inverse view-projection matrix float4x4 _PrevViewProj; // previous view-projection matrix float4x4 _ToPrevViewProjCombined; // combined float4x4 _StereoToPrevViewProjCombined0; // combined stereo versions. float4x4 _StereoToPrevViewProjCombined1; // combined stereo versions. float _Jitter; float _VelocityScale; float _DisplayVelocityScale; float _MaxVelocity; float _MinVelocity; float4 _BlurDirectionPacked; float _SoftZDistance; v2f vert(appdata_img v) { v2f o; o.pos = UnityObjectToClipPos(v.vertex); o.uv = UnityStereoScreenSpaceUVAdjust(v.texcoord.xy, _MainTex_ST); return o; } float4x4 GetPrevViewProjCombined() { #ifdef UNITY_SINGLE_PASS_STEREO return unity_StereoEyeIndex == 0 ? _StereoToPrevViewProjCombined0 : _StereoToPrevViewProjCombined1; #else return _ToPrevViewProjCombined; #endif } float4 CameraVelocity(v2f i) : SV_Target { float2 depth_uv = i.uv; #if UNITY_UV_STARTS_AT_TOP if (_MainTex_TexelSize.y < 0) depth_uv.y = 1 - depth_uv.y; #endif // read depth float d = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, depth_uv); // calculate position from pixel from depth float3 clipPos = float3(i.uv.x*2.0-1.0, (i.uv.y)*2.0-1.0, d); // only 1 matrix mul: float4 prevClipPos = mul(GetPrevViewProjCombined(), float4(clipPos, 1.0)); prevClipPos.xyz /= prevClipPos.w; /* float4 ws = mul(_InvViewProj, float4(clipPos, 1.0)); ws /= ws.w; prevClipPos = mul(_PrevViewProj,ws); prevClipPos.xyz /= prevClipPos.w; */ /* float2 vel = _VelocityScale *(clipPos.xy - prevClipPos.xy) / 2.f; // clamp to maximum velocity (in pixels) float maxVel = length(_MainTex_TexelSize.xy*_MaxVelocity); if (length(vel) > maxVel) { vel = normalize(vel) * maxVel; } return float4(vel, 0.0, 0.0); */ float2 vel = _MainTex_TexelSize.zw * _VelocityScale * (clipPos.xy - prevClipPos.xy) / 2.f; float vellen = length(vel); float maxVel = _MaxVelocity; float2 velOut = vel * max(0.5, min(vellen, maxVel)) / (vellen + 1e-2f); velOut *= _MainTex_TexelSize.xy; return float4(velOut, 0.0, 0.0); } // vector with largest magnitude float2 vmax(float2 a, float2 b) { float ma = dot(a, a); float mb = dot(b, b); return (ma > mb) ? a : b; } // find dominant velocity for each tile float4 TileMax(v2f i) : SV_Target { float2 uvCorner = i.uv - _MainTex_TexelSize.xy * (_MaxRadiusOrKInPaper * 0.5); float2 maxvel = float2(0,0); float4 baseUv = float4(uvCorner,0,0); float4 uvScale = float4(_MainTex_TexelSize.xy, 0, 0); for(int l=0; l<(int)_MaxRadiusOrKInPaper; l++) { for(int k=0; k<(int)_MaxRadiusOrKInPaper; k++) { maxvel = vmax(maxvel, tex2Dlod(_MainTex, baseUv + float4(l,k,0,0) * uvScale).xy); } } return float4(maxvel, 0, 1); } // find maximum velocity in any adjacent tile float4 NeighbourMax(v2f i) : SV_Target { float2 x_ = i.uv; // to fetch all neighbours, we need 3x3 point filtered samples float2 nx = tex2D(_MainTex, x_+float2(1.0, 1.0)*_MainTex_TexelSize.xy).xy; nx = vmax(nx, tex2D(_MainTex, x_+float2(1.0, 0.0)*_MainTex_TexelSize.xy).xy); nx = vmax(nx, tex2D(_MainTex, x_+float2(1.0,-1.0)*_MainTex_TexelSize.xy).xy); nx = vmax(nx, tex2D(_MainTex, x_+float2(0.0, 1.0)*_MainTex_TexelSize.xy).xy); nx = vmax(nx, tex2D(_MainTex, x_+float2(0.0, 0.0)*_MainTex_TexelSize.xy).xy); nx = vmax(nx, tex2D(_MainTex, x_+float2(0.0,-1.0)*_MainTex_TexelSize.xy).xy); nx = vmax(nx, tex2D(_MainTex, x_+float2(-1.0, 1.0)*_MainTex_TexelSize.xy).xy); nx = vmax(nx, tex2D(_MainTex, x_+float2(-1.0, 0.0)*_MainTex_TexelSize.xy).xy); nx = vmax(nx, tex2D(_MainTex, x_+float2(-1.0,-1.0)*_MainTex_TexelSize.xy).xy); return float4(nx, 0, 0); } float4 Debug(v2f i) : SV_Target { return saturate( float4(tex2D(_MainTex, i.uv).x,abs(tex2D(_MainTex, i.uv).y),-tex2D(_MainTex, i.uv).xy) * _DisplayVelocityScale); } // classification filters float cone(float2 px, float2 py, float2 v) { return clamp(1.0 - (length(px - py) / length(v)), 0.0, 1.0); } float cylinder(float2 x, float2 y, float2 v) { float lv = length(v); return 1.0 - smoothstep(0.95*lv, 1.05*lv, length(x - y)); } // is zb closer than za? float softDepthCompare(float za, float zb) { return clamp(1.0 - (za - zb) / _SoftZDistance, 0.0, 1.0); } float4 SimpleBlur (v2f i) : SV_Target { float2 x = i.uv; float2 xf = x; #if UNITY_UV_STARTS_AT_TOP if (_MainTex_TexelSize.y < 0) xf.y = 1 - xf.y; #endif float2 vx = tex2D(_VelTex, xf).xy; // vel at x float4 sum = float4(0, 0, 0, 0); for(int l=0; l _MaxVelocity) { blurDir *= (_MaxVelocity / velMag); velMag = _MaxVelocity; } float4 centerTap = tex2D(_MainTex, x); float4 sum = centerTap; blurDir *= smoothstep(_MinVelocity * 0.25f, _MinVelocity * 2.5, velMag); blurDir *= _MainTex_TexelSize.xy; blurDir /= MOTION_SAMPLES; for(int i=0; i