您最多选择25个主题 主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 
 

516 行
12 KiB

/*
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" {}
}
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;
float4x4 _InvViewProj; // inverse view-projection matrix
float4x4 _PrevViewProj; // previous view-projection matrix
float4x4 _ToPrevViewProjCombined; // combined
float _Jitter;
float _VelocityScale;
float _DisplayVelocityScale;
float _MaxVelocity;
float _MinVelocity;
float4 _BlurDirectionPacked;
float _SoftZDistance;
v2f vert(appdata_img v)
{
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
o.uv = v.texcoord.xy;
return o;
}
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(_ToPrevViewProjCombined, 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<NUM_SAMPLES; l++) {
float t = l / (float) (NUM_SAMPLES - 1);
t = t-0.5;
float2 y = x - vx*t;
float4 cy = tex2D(_MainTex, y);
sum += cy;
}
sum /= NUM_SAMPLES;
return sum;
}
float4 ReconstructFilterBlur(v2f i) : SV_Target
{
// uv's
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 x2 = xf;
float2 vn = tex2Dlod(_NeighbourMaxTex, float4(x2,0,0)).xy; // largest velocity in neighbourhood
float4 cx = tex2Dlod(_MainTex, float4(x,0,0)); // color at x
float2 vx = tex2Dlod(_VelTex, float4(xf,0,0)).xy; // vel at x
float zx = SAMPLE_DEPTH_TEXTURE_LOD(_CameraDepthTexture, float4(x,0,0));
zx = -Linear01Depth(zx);
// random offset [-0.5, 0.5]
float j = (tex2Dlod(_NoiseTex, float4(i.uv,0,0) * 11.0f).r*2-1) * _Jitter;
// sample current pixel
float weight = 0.75; // <= good start weight choice??
float4 sum = cx * weight;
int centerSample = (int)(NUM_SAMPLES-1)/2;
for(int l=0; l<NUM_SAMPLES; l++)
{
float contrib = 1.0f;
#if SHADER_API_D3D11
if (l==centerSample) continue; // skip center sample
#else
if (l==centerSample) contrib = 0.0f; // skip center sample
#endif
float t = lerp(-1.0, 1.0, (l + j) / (-1 + _Jitter + (float)NUM_SAMPLES));
//float t = lerp(-1.0, 1.0, l / (float)(NUM_SAMPLES - 1));
float2 y = x + vn * t;
float2 yf = y;
#if UNITY_UV_STARTS_AT_TOP
if (_MainTex_TexelSize.y < 0)
yf.y = 1-yf.y;
#endif
// velocity at y
float2 vy = tex2Dlod(_VelTex, float4(yf,0,0)).xy;
float zy = SAMPLE_DEPTH_TEXTURE_LOD(_CameraDepthTexture, float4(y,0,0));
zy = -Linear01Depth(zy);
float f = softDepthCompare(zx, zy);
float b = softDepthCompare(zy, zx);
float alphay = b * cone(x, y, vx) + f * cone(y, x, vy) + cylinder(y, x, vy) * cylinder(x, y, vx) * 2.0;
float4 cy = tex2Dlod(_MainTex, float4(y,0,0));
sum += cy * alphay * contrib;
weight += alphay * contrib;
}
sum /= weight;
return sum;
}
float4 ReconstructionDiscBlur (v2f i) : SV_Target
{
float2 xf = i.uv;
float2 x = i.uv;
#if UNITY_UV_STARTS_AT_TOP
if (_MainTex_TexelSize.y < 0)
xf.y = 1 - xf.y;
#endif
float2 x2 = xf;
float2 vn = tex2Dlod(_NeighbourMaxTex, float4(x2,0,0)).xy; // largest velocity in neighbourhood
float4 cx = tex2Dlod(_MainTex, float4(x,0,0)); // color at x
float2 vx = tex2Dlod(_VelTex, float4(xf,0,0)).xy; // vel at x
float4 noise = tex2Dlod(_NoiseTex, float4(i.uv,0,0)*11.0f)*2-1;
float zx = SAMPLE_DEPTH_TEXTURE_LOD(_CameraDepthTexture, float4(x,0,0));
zx = -Linear01Depth(zx);
noise *= _MainTex_TexelSize.xyxy * _Jitter;
//return abs(blurDir.xyxy)*10 + centerTap;
float weight = 1.0; // <- maybe tweak this: bluriness amount ...
float4 sum = cx * weight;
float4 jitteredDir = vn.xyxy + noise.xyyz;
#ifdef SHADER_API_D3D11
jitteredDir = max(abs(jitteredDir.xyxy), _MainTex_TexelSize.xyxy * _MaxVelocity * 0.5) * sign(jitteredDir.xyxy) * float4(1,1,-1,-1);
#else
jitteredDir = max(abs(jitteredDir.xyxy), _MainTex_TexelSize.xyxy * _MaxVelocity * 0.15) * sign(jitteredDir.xyxy) * float4(1,1,-1,-1);
#endif
for(int l=0; l<SmallDiscKernelSamples; l++)
{
float4 y = i.uv.xyxy + jitteredDir.xyxy * SmallDiscKernel[l].xyxy * float4(1,1,-1,-1);
float4 yf = y;
#if UNITY_UV_STARTS_AT_TOP
if (_MainTex_TexelSize.y < 0)
yf.yw = 1-yf.yw;
#endif
// velocity at y
float2 vy = tex2Dlod(_VelTex, float4(yf.xy,0,0)).xy;
float zy = SAMPLE_DEPTH_TEXTURE_LOD(_CameraDepthTexture, float4(y.xy,0,0) );
zy = -Linear01Depth(zy);
float f = softDepthCompare(zx, zy);
float b = softDepthCompare(zy, zx);
float alphay = b * cone(x, y.xy, vx) + f * cone(y.xy, x, vy) + cylinder(y.xy, x, vy) * cylinder(x, y.xy, vx) * 2.0;
float4 cy = tex2Dlod(_MainTex, float4(y.xy,0,0));
sum += cy * alphay;
weight += alphay;
#ifdef SHADER_API_D3D11
vy = tex2Dlod(_VelTex, float4(yf.zw,0,0)).xy;
zy = SAMPLE_DEPTH_TEXTURE_LOD(_CameraDepthTexture, float4(y.zw,0,0) );
zy = -Linear01Depth(zy);
f = softDepthCompare(zx, zy);
b = softDepthCompare(zy, zx);
alphay = b * cone(x, y.zw, vx) + f * cone(y.zw, x, vy) + cylinder(y.zw, x, vy) * cylinder(x, y.zw, vx) * 2.0;
cy = tex2Dlod(_MainTex, float4(y.zw,0,0));
sum += cy * alphay;
weight += alphay;
#endif
}
return sum / weight;
}
float4 MotionVectorBlur (v2f i) : SV_Target
{
float2 x = i.uv;
float2 insideVector = (x*2-1) * float2(1,_MainTex_TexelSize.w/_MainTex_TexelSize.z);
float2 rollVector = float2(insideVector.y, -insideVector.x);
float2 blurDir = _BlurDirectionPacked.x * float2(0,1);
blurDir += _BlurDirectionPacked.y * float2(1,0);
blurDir += _BlurDirectionPacked.z * rollVector;
blurDir += _BlurDirectionPacked.w * insideVector;
blurDir *= _VelocityScale;
// clamp to maximum velocity (in pixels)
float velMag = length(blurDir);
if (velMag > _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<MOTION_SAMPLES; i++) {
float4 tap = tex2D(_MainTex, x+i*blurDir);
sum += tap;
}
return sum/(1+MOTION_SAMPLES);
}
ENDCG
Subshader {
// pass 0
Pass {
ZTest Always Cull Off ZWrite On Blend Off
CGPROGRAM
#pragma target 3.0
#pragma vertex vert
#pragma fragment CameraVelocity
ENDCG
}
// pass 1
Pass {
ZTest Always Cull Off ZWrite Off Blend Off
CGPROGRAM
#pragma target 3.0
#pragma vertex vert
#pragma fragment Debug
ENDCG
}
// pass 2
Pass {
ZTest Always Cull Off ZWrite Off Blend Off
CGPROGRAM
#pragma target 3.0
#pragma vertex vert
#pragma fragment TileMax
ENDCG
}
// pass 3
Pass {
ZTest Always Cull Off ZWrite Off Blend Off
CGPROGRAM
#pragma target 3.0
#pragma vertex vert
#pragma fragment NeighbourMax
ENDCG
}
// pass 4
Pass {
ZTest Always Cull Off ZWrite Off Blend Off
CGPROGRAM
#pragma target 3.0
#pragma vertex vert
#pragma fragment ReconstructFilterBlur
ENDCG
}
// pass 5
Pass {
ZTest Always Cull Off ZWrite Off Blend Off
CGPROGRAM
#pragma target 3.0
#pragma vertex vert
#pragma fragment SimpleBlur
ENDCG
}
// pass 6
Pass {
ZTest Always Cull Off ZWrite Off Blend Off
CGPROGRAM
#pragma target 3.0
#pragma vertex vert
#pragma fragment MotionVectorBlur
ENDCG
}
// pass 7
Pass {
ZTest Always Cull Off ZWrite Off Blend Off
CGPROGRAM
#pragma target 3.0
#pragma vertex vert
#pragma fragment ReconstructionDiscBlur
ENDCG
}
}
Fallback off
}