// Depth of Field
// by Knu



// **
// ** ADJUSTABLE VARIABLES

#define SM ps_3_0 // Shader model: ps_3_0, ps_2_0
#define ROTATE // Random rotation on, if defined. ps_3_0 only

static float fr = 60.0; // retina focal point, dpt
// set slightly lower than fp to simulate myopia or as an alternative to MGE's distant blur
// set slightly higher than fp+fpa to simulate hyperopia

static float fp = 60.0; // eye relaxed focal power, dpt
static float fpa = 10.0; // accomodation, dpt
// set lower to simulate presbyopia

static float dmin = 0.003; // pupil diameter range, m
static float dmax = 0.009;

static float blur_radius = 1.0; // base blur radius;
// higher values mean more blur when out of DoF and shorter DoF. 
static float blur_falloff = 2.0; // More means more blur and less respect for edges

static float R = 8.0; // maximum blur radius in pixels;
static float FOV = 75.0; // Field of View. Change only if you use non-standard FOV

// ** END OF
// **


static float t =  2.0 * tan(radians(FOV * 0.5));
static float k = 0.00001;
static float unit2m = 0.0142;
static float eps = 0.000001;

float HDR;
float2 rcpres;



texture lastshader;
texture lastpass;
texture depthframe;

sampler s0 = sampler_state {texture=<lastshader>; };
sampler s1 = sampler_state {texture=<depthframe>; AddressU=Clamp; AddressV=Clamp;};
sampler s3 = sampler_state {texture=<lastpass>; AddressU=Mirror; AddressV=Mirror; MagFilter=Linear; MinFilter = Linear;};



#ifdef ROTATE
string texname1="noise64.tga";
texture tex1;
sampler s2 = sampler_state {texture=<tex1>; AddressU=Wrap; AddressV=Wrap;};
#endif



#define M 12
static float2 taps[M] =
{
    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)
};



float4 dof(in float2 tex : TEXCOORD ) : COLOR0
{
    float s = tex2D(s1, float2(0.5, 0.5)).r * unit2m;

    float z_corr = length(float3((tex.x - 0.5) * t, (tex.y - 0.5) * t / rcpres.y * rcpres.x, 1));
    float z = tex2D(s1, tex).r * z_corr * unit2m;

    float fpf = clamp(1 / s + fr, fp, fp + fpa);
    float c = lerp(dmax, dmin, HDR) * (fr - fpf + 1 / z) / fr / k * blur_radius;
    c = min(abs(c), R) / R * step(eps, z);

    return float4(tex2D(s0, tex).rgb, c);
}

float4 smartblur(in float2 tex : TEXCOORD ) : COLOR0
{

#ifdef ROTATE
    float2 rnd = normalize(tex2D(s2, tex / rcpres / 64).xy * 2 + float2(1, 1));
#endif

    float4 color = tex2D(s3, tex);
    float c = color.a * R;

    float amount = 1.0;
    for (int i = 0; i < M; i++)
    {

#ifdef ROTATE
        float2 dir = reflect(taps[i], rnd);
#else
        float2 dir = taps[i];
#endif

        float2 s_tex = tex + rcpres * dir * c;
        float4 s_color = tex2D(s3, s_tex);
        float s_c = s_color.a * R;
        float weight = exp2(-abs(c - s_c) / blur_falloff);

        color += s_color * weight;
        amount += weight;
    }
    return float4(color.rgb / amount, 1);
}



technique T0
{
    pass { PixelShader = compile SM dof(); }
    pass { PixelShader = compile SM smartblur(); }
}
